Skip to content

Commit

Permalink
Update select location screen
Browse files Browse the repository at this point in the history
Co-Authored-By: Boban Sijuk <[email protected]>
  • Loading branch information
MaryamShaghaghi and Boki91 committed Nov 27, 2023
1 parent 3ae3874 commit 9f6dfe0
Show file tree
Hide file tree
Showing 8 changed files with 238 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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(
Expand All @@ -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()

Expand Down Expand Up @@ -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()
Expand All @@ -146,7 +172,7 @@ fun SelectLocationScreen(
MaterialTheme.colorScheme.onBackground.copy(alpha = AlphaScrollbar)
),
state = lazyListState,
horizontalAlignment = Alignment.CenterHorizontally
horizontalAlignment = Alignment.CenterHorizontally,
) {
when (uiState) {
SelectLocationUiState.Loading -> {
Expand All @@ -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()
)
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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.
Expand All @@ -129,14 +132,15 @@ 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()) }
viewModel { WelcomeViewModel(get(), get(), get(), get()) }
viewModel { ReportProblemViewModel(get(), get()) }
viewModel { ViewLogsViewModel(get()) }
viewModel { OutOfTimeViewModel(get(), get(), get(), get()) }
viewModel { FilterViewModel(get()) }
}

const val SELF_PACKAGE_NAME = "SELF_PACKAGE_NAME"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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 =
Expand Down Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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()
}
Expand Down
Loading

0 comments on commit 9f6dfe0

Please sign in to comment.