diff options
5 files changed, 195 insertions, 1 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/communication/CustomListAction.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/communication/CustomListAction.kt new file mode 100644 index 0000000000..0b478f5272 --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/communication/CustomListAction.kt @@ -0,0 +1,33 @@ +package net.mullvad.mullvadvpn.compose.communication + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +sealed interface CustomListAction : Parcelable { + + @Parcelize + data class Rename(val customListId: String, val name: String, val newName: String) : + CustomListAction { + fun not() = this.copy(name = newName, newName = name) + } + + @Parcelize + data class Delete(val customListId: String) : CustomListAction { + fun not(name: String, locations: List<String>) = Create(name, locations) + } + + @Parcelize + data class Create(val name: String = "", val locations: List<String> = emptyList()) : + CustomListAction, Parcelable { + fun not(customListId: String) = Delete(customListId) + } + + @Parcelize + data class UpdateLocations( + val customListId: String, + val locations: List<String> = emptyList() + ) : CustomListAction { + fun not(locations: List<String>): UpdateLocations = + UpdateLocations(customListId = customListId, locations = locations) + } +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/communication/CustomListResult.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/communication/CustomListResult.kt new file mode 100644 index 0000000000..32fa077a7f --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/communication/CustomListResult.kt @@ -0,0 +1,34 @@ +package net.mullvad.mullvadvpn.compose.communication + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +sealed interface CustomListResult : Parcelable { + val undo: CustomListAction + + @Parcelize + data class Created( + val id: String, + val name: String, + val locationName: String?, + override val undo: CustomListAction.Delete + ) : CustomListResult + + @Parcelize + data class Deleted(override val undo: CustomListAction.Create) : CustomListResult { + val name + get() = undo.name + } + + @Parcelize + data class Renamed(override val undo: CustomListAction.Rename) : CustomListResult { + val name: String + get() = undo.name + } + + @Parcelize + data class LocationsChanged( + val name: String, + override val undo: CustomListAction.UpdateLocations + ) : CustomListResult +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/RelayListUseCase.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/RelayListUseCase.kt index ab3d93e06e..4957818283 100644 --- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/RelayListUseCase.kt +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/RelayListUseCase.kt @@ -44,13 +44,18 @@ class RelayListUseCase( findSelectedRelayItem( relaySettings = settings?.relaySettings, relayCountries = relayCountries, - customLists = customLists + customLists = customLists, ) RelayList(customLists, relayCountries, selectedItem) } fun selectedRelayItem(): Flow<RelayItem?> = relayListWithSelection().map { it.selectedItem } + fun relayList(): Flow<List<RelayItem.Country>> = relayListWithSelection().map { it.country } + + fun customLists(): Flow<List<RelayItem.CustomList>> = + relayListWithSelection().map { it.customLists } + fun fetchRelayList() { relayListListener.fetchRelayList() } diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/customlists/CustomListActionUseCase.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/customlists/CustomListActionUseCase.kt new file mode 100644 index 0000000000..7b2e5a43aa --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/customlists/CustomListActionUseCase.kt @@ -0,0 +1,117 @@ +package net.mullvad.mullvadvpn.usecase.customlists + +import kotlinx.coroutines.flow.firstOrNull +import net.mullvad.mullvadvpn.compose.communication.CustomListAction +import net.mullvad.mullvadvpn.compose.communication.CustomListResult +import net.mullvad.mullvadvpn.model.CreateCustomListResult +import net.mullvad.mullvadvpn.model.CustomList +import net.mullvad.mullvadvpn.model.GeographicLocationConstraint +import net.mullvad.mullvadvpn.model.UpdateCustomListResult +import net.mullvad.mullvadvpn.relaylist.getRelayItemsByCodes +import net.mullvad.mullvadvpn.repository.CustomListsRepository +import net.mullvad.mullvadvpn.usecase.RelayListUseCase + +class CustomListActionUseCase( + private val customListsRepository: CustomListsRepository, + private val relayListUseCase: RelayListUseCase +) { + suspend fun performAction(action: CustomListAction): Result<CustomListResult> { + return when (action) { + is CustomListAction.Create -> { + performAction(action) + } + is CustomListAction.Rename -> { + performAction(action) + } + is CustomListAction.Delete -> { + performAction(action) + } + is CustomListAction.UpdateLocations -> { + performAction(action) + } + } + } + + suspend fun performAction(action: CustomListAction.Rename): Result<CustomListResult.Renamed> = + when ( + val result = + customListsRepository.updateCustomListName(action.customListId, action.newName) + ) { + is UpdateCustomListResult.Ok -> + Result.success(CustomListResult.Renamed(undo = action.not())) + is UpdateCustomListResult.Error -> Result.failure(CustomListsException(result.error)) + } + + suspend fun performAction(action: CustomListAction.Create): Result<CustomListResult.Created> = + when (val result = customListsRepository.createCustomList(action.name)) { + is CreateCustomListResult.Ok -> { + if (action.locations.isNotEmpty()) { + customListsRepository.updateCustomListLocationsFromCodes( + result.id, + action.locations + ) + val locationNames = + relayListUseCase + .relayList() + .firstOrNull() + ?.getRelayItemsByCodes(action.locations) + ?.map { it.name } + Result.success( + CustomListResult.Created( + id = result.id, + name = action.name, + locationName = locationNames?.first(), + undo = action.not(result.id) + ) + ) + } else { + Result.success( + CustomListResult.Created( + id = result.id, + name = action.name, + locationName = null, + undo = action.not(result.id) + ) + ) + } + } + is CreateCustomListResult.Error -> Result.failure(CustomListsException(result.error)) + } + + fun performAction(action: CustomListAction.Delete): Result<CustomListResult.Deleted> { + val customList: CustomList? = customListsRepository.getCustomListById(action.customListId) + val oldLocations = customList.locations() + val name = customList?.name ?: "" + customListsRepository.deleteCustomList(action.customListId) + return Result.success( + CustomListResult.Deleted(undo = action.not(locations = oldLocations, name = name)) + ) + } + + suspend fun performAction( + action: CustomListAction.UpdateLocations + ): Result<CustomListResult.LocationsChanged> { + val customList: CustomList? = customListsRepository.getCustomListById(action.customListId) + val oldLocations = customList.locations() + val name = customList?.name ?: "" + customListsRepository.updateCustomListLocationsFromCodes( + action.customListId, + action.locations + ) + return Result.success( + CustomListResult.LocationsChanged( + name = name, + undo = action.not(locations = oldLocations) + ) + ) + } + + private fun CustomList?.locations(): List<String> = + this?.locations?.map { + when (it) { + is GeographicLocationConstraint.City -> it.cityCode + is GeographicLocationConstraint.Country -> it.countryCode + is GeographicLocationConstraint.Hostname -> it.hostname + } + } ?: emptyList() +} diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/customlists/CustomListsException.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/customlists/CustomListsException.kt new file mode 100644 index 0000000000..07c37f7333 --- /dev/null +++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/usecase/customlists/CustomListsException.kt @@ -0,0 +1,5 @@ +package net.mullvad.mullvadvpn.usecase.customlists + +import net.mullvad.mullvadvpn.model.CustomListsError + +class CustomListsException(val error: CustomListsError) : Throwable() |
