Skip to content

Commit

Permalink
Improve and expand upon result data
Browse files Browse the repository at this point in the history
  • Loading branch information
Pururun committed Jul 31, 2024
1 parent d9d2e29 commit 0ed5b27
Show file tree
Hide file tree
Showing 15 changed files with 274 additions and 133 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package net.mullvad.mullvadvpn.compose.communication

import android.os.Parcelable
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
import net.mullvad.mullvadvpn.lib.model.CustomListName

sealed interface CustomListActionResultData : Parcelable {
val undo: CustomListAction?

@Parcelize
data class CreatedWithLocations(
val customListName: CustomListName,
val locationNames: List<String>,
override val undo: CustomListAction
) : CustomListActionResultData

@Parcelize
data class Deleted(
val customListName: CustomListName,
override val undo: CustomListAction.Create
) : CustomListActionResultData

@Parcelize
data class Renamed(val newName: CustomListName, override val undo: CustomListAction) :
CustomListActionResultData

@Parcelize
data class LocationAdded(
val customListName: CustomListName,
val locationName: String,
override val undo: CustomListAction
) : CustomListActionResultData

@Parcelize
data class LocationRemoved(
val customListName: CustomListName,
val locationName: String,
override val undo: CustomListAction
) : CustomListActionResultData

@Parcelize
data class LocationChanged(
val customListName: CustomListName,
override val undo: CustomListAction
) : CustomListActionResultData

@Parcelize
data object GenericError : CustomListActionResultData {
@IgnoredOnParcel override val undo: CustomListAction? = null
}

fun hasUndo() = undo != null
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ import com.ramcosta.composedestinations.result.ResultBackNavigator
import com.ramcosta.composedestinations.spec.DestinationStyle
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.button.PrimaryButton
import net.mullvad.mullvadvpn.compose.communication.Created
import net.mullvad.mullvadvpn.compose.communication.CustomListActionResultData
import net.mullvad.mullvadvpn.compose.component.CustomListNameTextField
import net.mullvad.mullvadvpn.compose.state.CreateCustomListUiState
import net.mullvad.mullvadvpn.compose.test.CREATE_CUSTOM_LIST_DIALOG_INPUT_TEST_TAG
Expand Down Expand Up @@ -63,7 +63,7 @@ data class CreateCustomListNavArgs(val locationCode: GeoLocationId?)
)
fun CreateCustomList(
navigator: DestinationsNavigator,
backNavigator: ResultBackNavigator<Created>,
backNavigator: ResultBackNavigator<CustomListActionResultData.CreatedWithLocations>,
) {
val vm: CreateCustomListDialogViewModel = koinViewModel()
LaunchedEffect(key1 = Unit) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.result.ResultBackNavigator
import com.ramcosta.composedestinations.spec.DestinationStyle
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.communication.Deleted
import net.mullvad.mullvadvpn.compose.communication.CustomListActionResultData
import net.mullvad.mullvadvpn.compose.state.DeleteCustomListUiState
import net.mullvad.mullvadvpn.compose.util.LaunchedEffectCollect
import net.mullvad.mullvadvpn.lib.model.CustomListId
Expand Down Expand Up @@ -39,7 +39,7 @@ data class DeleteCustomListNavArgs(val customListId: CustomListId, val name: Cus
navArgs = DeleteCustomListNavArgs::class
)
fun DeleteCustomList(
navigator: ResultBackNavigator<Deleted>,
navigator: ResultBackNavigator<CustomListActionResultData.Deleted>,
) {
val viewModel: DeleteCustomListConfirmationViewModel = koinViewModel()
val state by viewModel.uiState.collectAsStateWithLifecycle()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import com.ramcosta.composedestinations.result.ResultBackNavigator
import com.ramcosta.composedestinations.spec.DestinationStyle
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.button.PrimaryButton
import net.mullvad.mullvadvpn.compose.communication.Renamed
import net.mullvad.mullvadvpn.compose.communication.CustomListActionResultData
import net.mullvad.mullvadvpn.compose.component.CustomListNameTextField
import net.mullvad.mullvadvpn.compose.state.EditCustomListNameUiState
import net.mullvad.mullvadvpn.compose.test.EDIT_CUSTOM_LIST_DIALOG_INPUT_TEST_TAG
Expand Down Expand Up @@ -50,7 +50,7 @@ data class EditCustomListNameNavArgs(
navArgs = EditCustomListNameNavArgs::class
)
fun EditCustomListName(
backNavigator: ResultBackNavigator<Renamed>,
backNavigator: ResultBackNavigator<CustomListActionResultData.Renamed>,
) {
val vm: EditCustomListNameDialogViewModel = koinViewModel()
LaunchedEffectCollect(vm.uiSideEffect) { sideEffect ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ import com.ramcosta.composedestinations.result.ResultRecipient
import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.cell.CheckableRelayLocationCell
import net.mullvad.mullvadvpn.compose.communication.LocationsChanged
import net.mullvad.mullvadvpn.compose.communication.CustomListActionResultData
import net.mullvad.mullvadvpn.compose.component.LocationsEmptyText
import net.mullvad.mullvadvpn.compose.component.MullvadCircularProgressIndicatorLarge
import net.mullvad.mullvadvpn.compose.component.NavigateBackIconButton
Expand Down Expand Up @@ -79,7 +79,7 @@ data class CustomListLocationsNavArgs(
)
fun CustomListLocations(
navigator: DestinationsNavigator,
backNavigator: ResultBackNavigator<LocationsChanged>,
backNavigator: ResultBackNavigator<CustomListActionResultData>,
discardChangesResultRecipient: ResultRecipient<DiscardChangesDestination, Boolean>,
) {
val customListsViewModel = koinViewModel<CustomListLocationsViewModel>()
Expand All @@ -99,9 +99,8 @@ fun CustomListLocations(
val context: Context = LocalContext.current
LaunchedEffectCollect(customListsViewModel.uiSideEffect) { sideEffect ->
when (sideEffect) {
is CustomListLocationsSideEffect.ReturnWithResult ->
is CustomListLocationsSideEffect.ReturnWithResultData ->
backNavigator.navigateBack(result = sideEffect.result)
CustomListLocationsSideEffect.CloseScreen -> backNavigator.navigateBack()
CustomListLocationsSideEffect.Error ->
launch {
snackbarHostState.showSnackbarImmediately(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import com.ramcosta.composedestinations.result.ResultRecipient
import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.cell.NavigationComposeCell
import net.mullvad.mullvadvpn.compose.communication.CustomListActionResultData
import net.mullvad.mullvadvpn.compose.communication.Deleted
import net.mullvad.mullvadvpn.compose.component.MullvadCircularProgressIndicatorLarge
import net.mullvad.mullvadvpn.compose.component.NavigateBackIconButton
Expand Down Expand Up @@ -63,7 +64,8 @@ private fun PreviewCustomListsScreen() {
@Destination<RootGraph>(style = SlideInFromRightTransition::class)
fun CustomLists(
navigator: DestinationsNavigator,
editCustomListResultRecipient: ResultRecipient<EditCustomListDestination, Deleted>
editCustomListResultRecipient:
ResultRecipient<EditCustomListDestination, CustomListActionResultData.Deleted>
) {
val viewModel = koinViewModel<CustomListsViewModel>()
val state by viewModel.uiState.collectAsStateWithLifecycle()
Expand All @@ -82,7 +84,7 @@ fun CustomLists(
message =
context.getString(
R.string.delete_custom_list_message,
result.value.name
result.value.customListName
),
actionLabel = context.getString(R.string.undo),
duration = SnackbarDuration.Long,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import com.ramcosta.composedestinations.result.ResultBackNavigator
import com.ramcosta.composedestinations.result.ResultRecipient
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.cell.TwoRowCell
import net.mullvad.mullvadvpn.compose.communication.CustomListActionResultData
import net.mullvad.mullvadvpn.compose.communication.Deleted
import net.mullvad.mullvadvpn.compose.component.MullvadCircularProgressIndicatorLarge
import net.mullvad.mullvadvpn.compose.component.NavigateBackIconButton
Expand Down Expand Up @@ -83,8 +84,9 @@ data class EditCustomListNavArgs(val customListId: CustomListId)
)
fun EditCustomList(
navigator: DestinationsNavigator,
backNavigator: ResultBackNavigator<Deleted>,
confirmDeleteListResultRecipient: ResultRecipient<DeleteCustomListDestination, Deleted>
backNavigator: ResultBackNavigator<CustomListActionResultData.Deleted>,
confirmDeleteListResultRecipient:
ResultRecipient<DeleteCustomListDestination, CustomListActionResultData.Deleted>
) {
val viewModel = koinViewModel<EditCustomListViewModel>()

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -68,12 +68,8 @@ import net.mullvad.mullvadvpn.compose.cell.IconCell
import net.mullvad.mullvadvpn.compose.cell.StatusRelayItemCell
import net.mullvad.mullvadvpn.compose.cell.SwitchComposeSubtitleCell
import net.mullvad.mullvadvpn.compose.cell.ThreeDotCell
import net.mullvad.mullvadvpn.compose.communication.Created
import net.mullvad.mullvadvpn.compose.communication.CustomListAction
import net.mullvad.mullvadvpn.compose.communication.CustomListSuccess
import net.mullvad.mullvadvpn.compose.communication.Deleted
import net.mullvad.mullvadvpn.compose.communication.LocationsChanged
import net.mullvad.mullvadvpn.compose.communication.Renamed
import net.mullvad.mullvadvpn.compose.communication.CustomListActionResultData
import net.mullvad.mullvadvpn.compose.component.LocationsEmptyText
import net.mullvad.mullvadvpn.compose.component.MullvadCircularProgressIndicatorLarge
import net.mullvad.mullvadvpn.compose.component.MullvadModalBottomSheet
Expand Down Expand Up @@ -132,12 +128,17 @@ private fun PreviewSelectLocationScreen() {
fun SelectLocation(
navigator: DestinationsNavigator,
backNavigator: ResultBackNavigator<Boolean>,
createCustomListDialogResultRecipient: ResultRecipient<CreateCustomListDestination, Created>,
createCustomListDialogResultRecipient:
ResultRecipient<
CreateCustomListDestination,
CustomListActionResultData.CreatedWithLocations
>,
editCustomListNameDialogResultRecipient:
ResultRecipient<EditCustomListNameDestination, Renamed>,
deleteCustomListDialogResultRecipient: ResultRecipient<DeleteCustomListDestination, Deleted>,
ResultRecipient<EditCustomListNameDestination, CustomListActionResultData.Renamed>,
deleteCustomListDialogResultRecipient:
ResultRecipient<DeleteCustomListDestination, CustomListActionResultData.Deleted>,
updateCustomListResultRecipient:
ResultRecipient<CustomListLocationsDestination, LocationsChanged>
ResultRecipient<CustomListLocationsDestination, CustomListActionResultData>
) {
val vm = koinViewModel<SelectLocationViewModel>()
val state = vm.uiState.collectAsStateWithLifecycle()
Expand All @@ -147,22 +148,12 @@ fun SelectLocation(
val lazyListState = rememberLazyListState()
CollectSideEffectWithLifecycle(vm.uiSideEffect) {
when (it) {
SelectLocationSideEffect.CloseScreen -> {
backNavigator.navigateBack(result = true)
}
is SelectLocationSideEffect.LocationAddedToCustomList ->
launch {
snackbarHostState.showResultSnackbar(
context = context,
result = it.result,
onUndo = vm::performAction
)
}
is SelectLocationSideEffect.LocationRemovedFromCustomList ->
SelectLocationSideEffect.CloseScreen -> backNavigator.navigateBack(result = true)
is SelectLocationSideEffect.CustomListActionToast ->
launch {
snackbarHostState.showResultSnackbar(
context = context,
result = it.result,
result = it.resultData,
onUndo = vm::performAction
)
}
Expand Down Expand Up @@ -335,7 +326,7 @@ fun SelectLocationScreen(

itemsIndexed(
items = state.relayListItems,
key = { index: Int, item: RelayListItem -> item.key },
key = { _: Int, item: RelayListItem -> item.key },
contentType = { _, item -> item.contentType },
itemContent = { index: Int, listItem: RelayListItem ->
Column(modifier = Modifier.animateItem()) {
Expand Down Expand Up @@ -834,45 +825,49 @@ private suspend fun LazyListState.animateScrollAndCentralizeItem(index: Int) {

private suspend fun SnackbarHostState.showResultSnackbar(
context: Context,
result: CustomListSuccess,
result: CustomListActionResultData,
onUndo: (CustomListAction) -> Unit
) {

showSnackbarImmediately(
message = result.message(context),
actionLabel = context.getString(R.string.undo),
actionLabel =
if (result.hasUndo()) context.getString(R.string.undo)
else {
null
},
duration = SnackbarDuration.Long,
onAction = { onUndo(result.undo) }
onAction = { result.undo?.let { onUndo(it) } }
)
}

private fun CustomListSuccess.message(context: Context): String =
private fun CustomListActionResultData.message(context: Context): String =
when (this) {
is Created ->
locationNames.firstOrNull()?.let { locationName ->
context.getString(R.string.location_was_added_to_list, locationName, name)
} ?: context.getString(R.string.locations_were_changed_for, name)
is Deleted -> context.getString(R.string.delete_custom_list_message, name)
is Renamed -> context.getString(R.string.name_was_changed_to, name)
is LocationsChanged ->
when {
addedLocations.size == 1 && removedLocations.isEmpty() ->
context.getString(
R.string.location_was_added_to_list,
addedLocations.first(),
name
)
removedLocations.size == 1 && addedLocations.isEmpty() ->
context.getString(
R.string.location_was_removed_from_list,
removedLocations.first(),
name
)
else -> context.getString(R.string.locations_were_changed_for, name)
is CustomListActionResultData.CreatedWithLocations ->
if (locationNames.size == 1) {
context.getString(
R.string.location_was_added_to_list,
locationNames.first(),
customListName
)
} else {
context.getString(R.string.create_custom_list_message, customListName)
}
is CustomListActionResultData.Deleted ->
context.getString(R.string.delete_custom_list_message, customListName)
is CustomListActionResultData.LocationAdded ->
context.getString(R.string.location_was_added_to_list, locationName, customListName)
is CustomListActionResultData.LocationRemoved ->
context.getString(R.string.location_was_removed_from_list, locationName, customListName)
is CustomListActionResultData.LocationChanged ->
context.getString(R.string.locations_were_changed_for, customListName)
is CustomListActionResultData.Renamed ->
context.getString(R.string.name_was_changed_to, newName)
CustomListActionResultData.GenericError -> context.getString(R.string.error_occurred)
}

@Composable
private fun <D : DestinationSpec, R : CustomListSuccess> ResultRecipient<D, R>
private fun <D : DestinationSpec, R : CustomListActionResultData> ResultRecipient<D, R>
.OnCustomListNavResult(
snackbarHostState: SnackbarHostState,
performAction: (action: CustomListAction) -> Unit
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import net.mullvad.mullvadvpn.lib.daemon.grpc.ManagementService
import net.mullvad.mullvadvpn.lib.model.Constraint
import net.mullvad.mullvadvpn.lib.model.GeoLocationId
import net.mullvad.mullvadvpn.lib.model.PortRange
import net.mullvad.mullvadvpn.lib.model.RelayItem
import net.mullvad.mullvadvpn.lib.model.RelayItemId
import net.mullvad.mullvadvpn.lib.model.WireguardConstraints
import net.mullvad.mullvadvpn.lib.model.WireguardEndpointData
import net.mullvad.mullvadvpn.relaylist.findByGeoLocationId

class RelayListRepository(
private val managementService: ManagementService,
Expand Down Expand Up @@ -49,5 +51,7 @@ class RelayListRepository(
suspend fun updateSelectedWireguardConstraints(value: WireguardConstraints) =
managementService.setWireguardConstraints(value)

fun find(geoLocationId: GeoLocationId) = relayList.value.findByGeoLocationId(geoLocationId)

private fun defaultWireguardEndpointData() = WireguardEndpointData(emptyList())
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.compose.communication.Created
import net.mullvad.mullvadvpn.compose.communication.CustomListAction
import net.mullvad.mullvadvpn.compose.communication.CustomListActionResultData
import net.mullvad.mullvadvpn.compose.state.CreateCustomListUiState
import net.mullvad.mullvadvpn.lib.model.CustomListId
import net.mullvad.mullvadvpn.lib.model.CustomListName
Expand Down Expand Up @@ -58,7 +58,13 @@ class CreateCustomListDialogViewModel(
)
} else {
_uiSideEffect.send(
CreateCustomListDialogSideEffect.ReturnWithResult(it)
CreateCustomListDialogSideEffect.ReturnWithResult(
CustomListActionResultData.CreatedWithLocations(
customListName = it.name,
locationNames = it.locationNames,
undo = it.undo
)
)
)
}
}
Expand All @@ -76,5 +82,6 @@ sealed interface CreateCustomListDialogSideEffect {
data class NavigateToCustomListLocationsScreen(val customListId: CustomListId) :
CreateCustomListDialogSideEffect

data class ReturnWithResult(val result: Created) : CreateCustomListDialogSideEffect
data class ReturnWithResult(val result: CustomListActionResultData.CreatedWithLocations) :
CreateCustomListDialogSideEffect
}
Loading

0 comments on commit 0ed5b27

Please sign in to comment.