summaryrefslogtreecommitdiffhomepage
path: root/android/app
diff options
context:
space:
mode:
authorMaryamShaghaghi <122574719+MaryamShaghaghi@users.noreply.github.com>2023-11-27 14:35:46 +0100
committerMaryamShaghaghi <122574719+MaryamShaghaghi@users.noreply.github.com>2023-12-04 11:30:23 +0100
commitba4658d9545d926bfe08341a5bc80c63b3a09d3e (patch)
treeafebb8532690566607385ef09ad5421f64b9c18c /android/app
parent63025aaa3152c4bddea0b960bf663c9a5797d59d (diff)
downloadmullvadvpn-ba4658d9545d926bfe08341a5bc80c63b3a09d3e.tar.xz
mullvadvpn-ba4658d9545d926bfe08341a5bc80c63b3a09d3e.zip
Update select location screen
Co-Authored-By: Boban Sijuk <49131853+boki91@users.noreply.github.com>
Diffstat (limited to 'android/app')
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreen.kt123
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/SelectLocationUiState.kt15
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/SearchTextField.kt18
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/di/UiModule.kt6
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt16
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SelectLocationFragment.kt8
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt101
7 files changed, 225 insertions, 62 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreen.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreen.kt
index c09a0b986a..55936b392a 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreen.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/screen/SelectLocationScreen.kt
@@ -30,16 +30,16 @@ import androidx.compose.ui.focus.focusRequester
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
-import androidx.compose.ui.unit.sp
import androidx.core.text.HtmlCompat
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import net.mullvad.mullvadvpn.R
+import net.mullvad.mullvadvpn.compose.cell.FilterCell
import net.mullvad.mullvadvpn.compose.cell.RelayLocationCell
import net.mullvad.mullvadvpn.compose.component.MullvadCircularProgressIndicatorLarge
import net.mullvad.mullvadvpn.compose.component.drawVerticalScrollbar
@@ -60,8 +60,11 @@ import net.mullvad.mullvadvpn.relaylist.RelayItem
private fun PreviewSelectLocationScreen() {
val state =
SelectLocationUiState.ShowData(
+ searchTerm = "",
countries = listOf(RelayCountry("Country 1", "Code 1", false, emptyList())),
- selectedRelay = null
+ selectedRelay = null,
+ selectedOwnership = null,
+ selectedProvidersCount = 0
)
AppTheme {
SelectLocationScreen(
@@ -80,8 +83,12 @@ fun SelectLocationScreen(
enterTransitionEndAction: SharedFlow<Unit>,
onSelectRelay: (item: RelayItem) -> Unit = {},
onSearchTermInput: (searchTerm: String) -> Unit = {},
- onBackClick: () -> Unit = {}
+ onBackClick: () -> Unit = {},
+ onFilterClick: () -> Unit = {},
+ removeOwnershipFilter: () -> Unit = {},
+ removeProviderFilter: () -> Unit = {}
) {
+
val backgroundColor = MaterialTheme.colorScheme.background
val systemUiController = rememberSystemUiController()
@@ -121,10 +128,29 @@ fun SelectLocationScreen(
.weight(weight = 1f)
.padding(end = Dimens.titleIconSize),
textAlign = TextAlign.Center,
- style = MaterialTheme.typography.headlineSmall.copy(fontSize = 20.sp),
+ style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.onPrimary
)
+ Image(
+ painter = painterResource(id = R.drawable.icons_more_circle),
+ contentDescription = null,
+ modifier = Modifier.size(Dimens.titleIconSize).clickable { onFilterClick() }
+ )
+ }
+ when (uiState) {
+ SelectLocationUiState.Loading -> {}
+ is SelectLocationUiState.ShowData -> {
+ if (uiState.hasFilter) {
+ FilterCell(
+ ownershipFilter = uiState.selectedOwnership,
+ selectedProviderFilter = uiState.selectedProvidersCount,
+ removeOwnershipFilter = removeOwnershipFilter,
+ removeProviderFilter = removeProviderFilter
+ )
+ }
+ }
}
+
SearchTextField(
modifier =
Modifier.fillMaxWidth()
@@ -146,7 +172,7 @@ fun SelectLocationScreen(
MaterialTheme.colorScheme.onBackground.copy(alpha = AlphaScrollbar)
),
state = lazyListState,
- horizontalAlignment = Alignment.CenterHorizontally
+ horizontalAlignment = Alignment.CenterHorizontally,
) {
when (uiState) {
SelectLocationUiState.Loading -> {
@@ -157,45 +183,56 @@ fun SelectLocationScreen(
}
}
is SelectLocationUiState.ShowData -> {
- items(
- count = uiState.countries.size,
- key = { index -> uiState.countries[index].hashCode() },
- contentType = { ContentType.ITEM }
- ) { index ->
- val country = uiState.countries[index]
- RelayLocationCell(
- relay = country,
- selectedItem = uiState.selectedRelay,
- onSelectRelay = onSelectRelay,
- modifier = Modifier.animateContentSize()
- )
- }
- }
- is SelectLocationUiState.NoSearchResultFound -> {
- item(contentType = ContentType.EMPTY_TEXT) {
- val firstRow =
- HtmlCompat.fromHtml(
- textResource(
- id = R.string.select_location_empty_text_first_row,
- uiState.searchTerm
- ),
- HtmlCompat.FROM_HTML_MODE_COMPACT
- )
- .toAnnotatedString(boldFontWeight = FontWeight.ExtraBold)
- Text(
- text =
- buildAnnotatedString {
- append(firstRow)
- appendLine()
- append(
+ if (uiState.countries.isEmpty()) {
+ item(contentType = ContentType.EMPTY_TEXT) {
+ val firstRow =
+ HtmlCompat.fromHtml(
textResource(
- id = R.string.select_location_empty_text_second_row
- )
+ id = R.string.select_location_empty_text_first_row,
+ uiState.searchTerm
+ ),
+ HtmlCompat.FROM_HTML_MODE_COMPACT
)
- },
- style = MaterialTheme.typography.labelMedium,
- textAlign = TextAlign.Center
- )
+ .toAnnotatedString(boldFontWeight = FontWeight.ExtraBold)
+ val secondRow =
+ textResource(id = R.string.select_location_empty_text_second_row)
+ Column(
+ modifier =
+ Modifier.padding(
+ horizontal = Dimens.selectLocationTitlePadding
+ ),
+ horizontalAlignment = Alignment.CenterHorizontally
+ ) {
+ Text(
+ text = firstRow,
+ style = MaterialTheme.typography.labelMedium,
+ textAlign = TextAlign.Center,
+ color = MaterialTheme.colorScheme.onSecondary,
+ maxLines = 2,
+ overflow = TextOverflow.Ellipsis
+ )
+ Text(
+ text = secondRow,
+ style = MaterialTheme.typography.labelMedium,
+ textAlign = TextAlign.Center,
+ color = MaterialTheme.colorScheme.onSecondary
+ )
+ }
+ }
+ } else {
+ items(
+ count = uiState.countries.size,
+ key = { index -> uiState.countries[index].hashCode() },
+ contentType = { ContentType.ITEM }
+ ) { index ->
+ val country = uiState.countries[index]
+ RelayLocationCell(
+ relay = country,
+ selectedItem = uiState.selectedRelay,
+ onSelectRelay = onSelectRelay,
+ modifier = Modifier.animateContentSize()
+ )
+ }
}
}
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/SelectLocationUiState.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/SelectLocationUiState.kt
index fece45f0aa..123bf821e6 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/SelectLocationUiState.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/state/SelectLocationUiState.kt
@@ -1,13 +1,20 @@
package net.mullvad.mullvadvpn.compose.state
+import net.mullvad.mullvadvpn.model.Ownership
import net.mullvad.mullvadvpn.relaylist.RelayCountry
import net.mullvad.mullvadvpn.relaylist.RelayItem
sealed interface SelectLocationUiState {
- data object Loading : SelectLocationUiState
- data class ShowData(val countries: List<RelayCountry>, val selectedRelay: RelayItem?) :
- SelectLocationUiState
+ data object Loading : SelectLocationUiState
- data class NoSearchResultFound(val searchTerm: String) : SelectLocationUiState
+ data class ShowData(
+ val searchTerm: String,
+ val countries: List<RelayCountry>,
+ val selectedRelay: RelayItem?,
+ val selectedOwnership: Ownership?,
+ val selectedProvidersCount: Int?
+ ) : SelectLocationUiState {
+ val hasFilter: Boolean = (selectedProvidersCount != null || selectedOwnership != null)
+ }
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/SearchTextField.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/SearchTextField.kt
index 2f743a8d23..bbee4a969b 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/SearchTextField.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/compose/textfield/SearchTextField.kt
@@ -2,6 +2,7 @@ package net.mullvad.mullvadvpn.compose.textfield
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
@@ -80,15 +81,28 @@ fun SearchTextField(
modifier =
Modifier.size(
width = Dimens.searchIconSize,
- height = Dimens.searchIconSize
+ height = Dimens.searchIconSize,
),
colorFilter =
- ColorFilter.tint(color = MaterialTheme.colorScheme.onSecondary)
+ ColorFilter.tint(color = MaterialTheme.colorScheme.onSecondary),
)
},
placeholder = {
Text(text = placeHolder, style = MaterialTheme.typography.labelLarge)
},
+ trailingIcon = {
+ if (searchTerm.isNotEmpty()) {
+ Image(
+ modifier =
+ Modifier.size(Dimens.smallIconSize).clickable {
+ searchTerm = ""
+ onValueChange.invoke(searchTerm)
+ },
+ painter = painterResource(id = R.drawable.icon_close),
+ contentDescription = null,
+ )
+ }
+ },
shape = MaterialTheme.shapes.medium,
colors =
TextFieldDefaults.colors(
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 b39d16b0aa..220097a731 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
@@ -29,6 +29,7 @@ import net.mullvad.mullvadvpn.usecase.NewDeviceNotificationUseCase
import net.mullvad.mullvadvpn.usecase.PaymentUseCase
import net.mullvad.mullvadvpn.usecase.PlayPaymentUseCase
import net.mullvad.mullvadvpn.usecase.PortRangeUseCase
+import net.mullvad.mullvadvpn.usecase.RelayListFilterUseCase
import net.mullvad.mullvadvpn.usecase.RelayListUseCase
import net.mullvad.mullvadvpn.usecase.TunnelStateNotificationUseCase
import net.mullvad.mullvadvpn.usecase.VersionNotificationUseCase
@@ -39,6 +40,7 @@ import net.mullvad.mullvadvpn.viewmodel.ChangelogViewModel
import net.mullvad.mullvadvpn.viewmodel.ConnectViewModel
import net.mullvad.mullvadvpn.viewmodel.DeviceListViewModel
import net.mullvad.mullvadvpn.viewmodel.DeviceRevokedViewModel
+import net.mullvad.mullvadvpn.viewmodel.FilterViewModel
import net.mullvad.mullvadvpn.viewmodel.LoginViewModel
import net.mullvad.mullvadvpn.viewmodel.OutOfTimeViewModel
import net.mullvad.mullvadvpn.viewmodel.PrivacyDisclaimerViewModel
@@ -103,6 +105,7 @@ val uiModule = module {
single<IChangelogDataProvider> { ChangelogDataProvider(get()) }
+ single { RelayListFilterUseCase(get(), get()) }
single { RelayListListener(get()) }
// Will be resolved using from either of the two PaymentModule.kt classes.
@@ -129,7 +132,7 @@ val uiModule = module {
viewModel { DeviceRevokedViewModel(get(), get()) }
viewModel { LoginViewModel(get(), get(), get()) }
viewModel { PrivacyDisclaimerViewModel(get()) }
- viewModel { SelectLocationViewModel(get(), get()) }
+ viewModel { SelectLocationViewModel(get(), get(), get()) }
viewModel { SettingsViewModel(get(), get()) }
viewModel { VoucherDialogViewModel(get(), get()) }
viewModel { VpnSettingsViewModel(get(), get(), get(), get(), get()) }
@@ -137,6 +140,7 @@ val uiModule = module {
viewModel { ReportProblemViewModel(get(), get()) }
viewModel { ViewLogsViewModel(get()) }
viewModel { OutOfTimeViewModel(get(), get(), get(), get()) }
+ viewModel { FilterViewModel(get()) }
}
const val SELF_PACKAGE_NAME = "SELF_PACKAGE_NAME"
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt
index f299b8c956..f5e24dacf1 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/MainActivity.kt
@@ -42,6 +42,7 @@ import net.mullvad.mullvadvpn.repository.PrivacyDisclaimerRepository
import net.mullvad.mullvadvpn.ui.fragment.AccountFragment
import net.mullvad.mullvadvpn.ui.fragment.ConnectFragment
import net.mullvad.mullvadvpn.ui.fragment.DeviceRevokedFragment
+import net.mullvad.mullvadvpn.ui.fragment.FilterFragment
import net.mullvad.mullvadvpn.ui.fragment.LoadingFragment
import net.mullvad.mullvadvpn.ui.fragment.LoginFragment
import net.mullvad.mullvadvpn.ui.fragment.OutOfTimeFragment
@@ -55,7 +56,6 @@ import net.mullvad.mullvadvpn.viewmodel.ChangelogDialogUiState
import net.mullvad.mullvadvpn.viewmodel.ChangelogViewModel
import org.koin.android.ext.android.getKoin
import org.koin.core.context.loadKoinModules
-import org.koin.dsl.bind
open class MainActivity : FragmentActivity() {
private val requestNotificationPermissionLauncher =
@@ -174,6 +174,20 @@ open class MainActivity : FragmentActivity() {
}
}
+ fun openFilter() {
+ supportFragmentManager.beginTransaction().apply {
+ setCustomAnimations(
+ R.anim.fragment_enter_from_right,
+ R.anim.do_nothing,
+ R.anim.do_nothing,
+ R.anim.fragment_exit_to_right
+ )
+ replace(R.id.main_fragment, FilterFragment())
+ addToBackStack(null)
+ commitAllowingStateLoss()
+ }
+ }
+
private fun launchDeviceStateHandler(): Job {
return lifecycleScope.launch {
launch {
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SelectLocationFragment.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SelectLocationFragment.kt
index d1c4ac72bf..64fdee71f6 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SelectLocationFragment.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/ui/fragment/SelectLocationFragment.kt
@@ -9,6 +9,7 @@ import androidx.compose.ui.platform.ComposeView
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.screen.SelectLocationScreen
import net.mullvad.mullvadvpn.lib.theme.AppTheme
+import net.mullvad.mullvadvpn.ui.MainActivity
import net.mullvad.mullvadvpn.viewmodel.SelectLocationViewModel
import org.koin.androidx.viewmodel.ext.android.viewModel
@@ -32,12 +33,19 @@ class SelectLocationFragment : BaseFragment() {
onSelectRelay = vm::selectRelay,
onSearchTermInput = vm::onSearchTermInput,
onBackClick = { activity?.onBackPressedDispatcher?.onBackPressed() },
+ removeOwnershipFilter = vm::removeOwnerFilter,
+ removeProviderFilter = vm::removeProviderFilter,
+ onFilterClick = ::openFilterView
)
}
}
}
}
+ private fun openFilterView() {
+ (context as? MainActivity)?.openFilter()
+ }
+
override fun onEnterTransitionAnimationEnd() {
vm.onTransitionAnimationEnd()
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt
index 5e95674e0a..caddae313b 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/viewmodel/SelectLocationViewModel.kt
@@ -7,37 +7,75 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import net.mullvad.mullvadvpn.compose.state.SelectLocationUiState
+import net.mullvad.mullvadvpn.compose.state.toNullableOwnership
+import net.mullvad.mullvadvpn.compose.state.toSelectedProviders
+import net.mullvad.mullvadvpn.model.Constraint
+import net.mullvad.mullvadvpn.model.Ownership
+import net.mullvad.mullvadvpn.relaylist.Provider
import net.mullvad.mullvadvpn.relaylist.RelayItem
import net.mullvad.mullvadvpn.relaylist.filterOnSearchTerm
import net.mullvad.mullvadvpn.ui.serviceconnection.ServiceConnectionManager
import net.mullvad.mullvadvpn.ui.serviceconnection.connectionProxy
+import net.mullvad.mullvadvpn.usecase.RelayListFilterUseCase
import net.mullvad.mullvadvpn.usecase.RelayListUseCase
class SelectLocationViewModel(
private val serviceConnectionManager: ServiceConnectionManager,
- private val relayListUseCase: RelayListUseCase
+ private val relayListUseCase: RelayListUseCase,
+ private val relayListFilterUseCase: RelayListFilterUseCase
) : ViewModel() {
+
private val _closeAction = MutableSharedFlow<Unit>()
private val _enterTransitionEndAction = MutableSharedFlow<Unit>()
private val _searchTerm = MutableStateFlow(EMPTY_SEARCH_TERM)
val uiState =
- combine(relayListUseCase.relayListWithSelection(), _searchTerm) {
+ combine(
+ relayListUseCase.relayListWithSelection(),
+ _searchTerm,
+ relayListFilterUseCase.selectedOwnership(),
+ relayListFilterUseCase.availableProviders(),
+ relayListFilterUseCase.selectedProviders()
+ ) {
(relayCountries, relayItem),
- searchTerm ->
+ searchTerm,
+ selectedOwnership,
+ allProviders,
+ selectedConstraintProviders ->
+ val selectedProviders =
+ selectedConstraintProviders.toSelectedProviders(allProviders)
+
+ val selectedProvidersByOwnershipList =
+ filterSelectedProvidersByOwnership(
+ selectedProviders,
+ selectedOwnership.toNullableOwnership()
+ )
+
+ val allProvidersByOwnershipListList =
+ filterAllProvidersByOwnership(
+ allProviders,
+ selectedOwnership.toNullableOwnership()
+ )
+
val filteredRelayCountries =
relayCountries.filterOnSearchTerm(searchTerm, relayItem)
- if (searchTerm.isNotEmpty() && filteredRelayCountries.isEmpty()) {
- SelectLocationUiState.NoSearchResultFound(searchTerm = searchTerm)
- } else {
- SelectLocationUiState.ShowData(
- countries = filteredRelayCountries,
- selectedRelay = relayItem
- )
- }
+ SelectLocationUiState.ShowData(
+ searchTerm = searchTerm,
+ countries = filteredRelayCountries,
+ selectedRelay = relayItem,
+ selectedOwnership = selectedOwnership.toNullableOwnership(),
+ selectedProvidersCount =
+ if (
+ selectedProvidersByOwnershipList.size ==
+ allProvidersByOwnershipListList.size
+ )
+ null
+ else selectedProvidersByOwnershipList.size
+ )
}
.stateIn(
viewModelScope,
@@ -47,6 +85,7 @@ class SelectLocationViewModel(
@Suppress("konsist.ensure public properties use permitted names")
val uiCloseAction = _closeAction.asSharedFlow()
+
@Suppress("konsist.ensure public properties use permitted names")
val enterTransitionEndAction = _enterTransitionEndAction.asSharedFlow()
@@ -64,6 +103,46 @@ class SelectLocationViewModel(
viewModelScope.launch { _searchTerm.emit(searchTerm) }
}
+ private fun filterSelectedProvidersByOwnership(
+ selectedProviders: List<Provider>,
+ selectedOwnership: Ownership?
+ ): List<Provider> {
+ return when (selectedOwnership) {
+ Ownership.MullvadOwned -> selectedProviders.filter { it.mullvadOwned }
+ Ownership.Rented -> selectedProviders.filterNot { it.mullvadOwned }
+ else -> selectedProviders
+ }
+ }
+
+ private fun filterAllProvidersByOwnership(
+ allProviders: List<Provider>,
+ selectedOwnership: Ownership?
+ ): List<Provider> {
+ return when (selectedOwnership) {
+ Ownership.MullvadOwned -> allProviders.filter { it.mullvadOwned }
+ Ownership.Rented -> allProviders.filterNot { it.mullvadOwned }
+ else -> allProviders
+ }
+ }
+
+ fun removeOwnerFilter() {
+ viewModelScope.launch {
+ relayListFilterUseCase.updateOwnershipAndProviderFilter(
+ Constraint.Any(),
+ relayListFilterUseCase.selectedProviders().first()
+ )
+ }
+ }
+
+ fun removeProviderFilter() {
+ viewModelScope.launch {
+ relayListFilterUseCase.updateOwnershipAndProviderFilter(
+ relayListFilterUseCase.selectedOwnership().first(),
+ Constraint.Any()
+ )
+ }
+ }
+
companion object {
private const val EMPTY_SEARCH_TERM = ""
}