Skip to content

Commit

Permalink
Rework sheets into destinations
Browse files Browse the repository at this point in the history
  • Loading branch information
Rawa committed Jul 22, 2024
1 parent db67d44 commit 0f6a02c
Show file tree
Hide file tree
Showing 24 changed files with 857 additions and 633 deletions.
1 change: 1 addition & 0 deletions android/app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -339,6 +339,7 @@ dependencies {
implementation(Dependencies.Compose.ui)
implementation(Dependencies.Compose.uiUtil)
implementation(Dependencies.Compose.destinations)
implementation(Dependencies.Compose.destinationsBottomSheet)
ksp(Dependencies.Compose.destinationsKsp)

implementation(Dependencies.jodaTime)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package net.mullvad.mullvadvpn.compose.bottomsheet

import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.bottomsheet.spec.DestinationStyleBottomSheet
import com.ramcosta.composedestinations.result.ResultBackNavigator
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.cell.HeaderCell
import net.mullvad.mullvadvpn.compose.cell.IconCell
import net.mullvad.mullvadvpn.compose.communication.CustomListActionResult
import net.mullvad.mullvadvpn.compose.component.MullvadModalBottomContainer
import net.mullvad.mullvadvpn.compose.util.CollectSideEffectWithLifecycle
import net.mullvad.mullvadvpn.lib.model.CustomListId
import net.mullvad.mullvadvpn.lib.model.GeoLocationId
import net.mullvad.mullvadvpn.viewmodel.CustomListEntrySheetSideEffect
import net.mullvad.mullvadvpn.viewmodel.CustomListEntrySheetViewModel
import org.koin.androidx.compose.koinViewModel

data class CustomListEntrySheetNavArgs(
val name: String,
val customListId: CustomListId,
val location: GeoLocationId
)

@OptIn(ExperimentalMaterial3Api::class)
@Destination<RootGraph>(
navArgs = CustomListEntrySheetNavArgs::class,
style = DestinationStyleBottomSheet::class
)
@Composable
fun CustomListEntrySheet(backNavigator: ResultBackNavigator<CustomListActionResult>) {
val vm = koinViewModel<CustomListEntrySheetViewModel>()
val state = vm.uiState.collectAsStateWithLifecycle()
CollectSideEffectWithLifecycle(vm.uiSideEffect) {
when (it) {
is CustomListEntrySheetSideEffect.LocationRemovedResult ->
backNavigator.navigateBack(it.result)
}
}
MullvadModalBottomContainer {
HeaderCell(
text =
stringResource(id = R.string.remove_location_from_list, state.value.locationName),
background = Color.Unspecified
)
HorizontalDivider(color = MaterialTheme.colorScheme.onBackground)

IconCell(
iconId = R.drawable.ic_remove,
title = stringResource(id = R.string.remove_button),
titleColor = MaterialTheme.colorScheme.onBackground,
onClick = vm::removeLocationFromList,
background = Color.Unspecified
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package net.mullvad.mullvadvpn.compose.bottomsheet

import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.bottomsheet.spec.DestinationStyleBottomSheet
import com.ramcosta.composedestinations.generated.destinations.CreateCustomListDestination
import com.ramcosta.composedestinations.generated.destinations.CustomListsDestination
import com.ramcosta.composedestinations.generated.destinations.CustomListsSheetDestination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.cell.HeaderCell
import net.mullvad.mullvadvpn.compose.cell.IconCell
import net.mullvad.mullvadvpn.compose.component.MullvadModalBottomContainer
import net.mullvad.mullvadvpn.lib.theme.color.AlphaInactive
import net.mullvad.mullvadvpn.lib.theme.color.AlphaVisible

@OptIn(ExperimentalMaterial3Api::class)
@Destination<RootGraph>(style = DestinationStyleBottomSheet::class)
@Composable
fun CustomListsSheet(navigator: DestinationsNavigator, editListEnabled: Boolean) {
MullvadModalBottomContainer {
HeaderCell(
text = stringResource(id = R.string.edit_custom_lists),
background = Color.Unspecified
)
HorizontalDivider(color = MaterialTheme.colorScheme.onBackground)
IconCell(
iconId = R.drawable.icon_add,
title = stringResource(id = R.string.new_list),
titleColor = MaterialTheme.colorScheme.onBackground,
onClick = {
navigator.navigate(CreateCustomListDestination(null)) {
popUpTo(CustomListsSheetDestination) { inclusive = true }
}
},
background = Color.Unspecified
)
IconCell(
iconId = R.drawable.icon_edit,
title = stringResource(id = R.string.edit_lists),
titleColor =
MaterialTheme.colorScheme.onBackground.copy(
alpha =
if (editListEnabled) {
AlphaVisible
} else {
AlphaInactive
}
),
onClick = { navigator.navigate(CustomListsDestination) },
background = Color.Unspecified,
enabled = editListEnabled
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
package net.mullvad.mullvadvpn.compose.bottomsheet

import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.compose.dropUnlessResumed
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.bottomsheet.spec.DestinationStyleBottomSheet
import com.ramcosta.composedestinations.generated.destinations.CustomListLocationsDestination
import com.ramcosta.composedestinations.generated.destinations.CustomListSheetDestination
import com.ramcosta.composedestinations.generated.destinations.DeleteCustomListDestination
import com.ramcosta.composedestinations.generated.destinations.EditCustomListNameDestination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.cell.HeaderCell
import net.mullvad.mullvadvpn.compose.cell.IconCell
import net.mullvad.mullvadvpn.compose.component.MullvadModalBottomContainer
import net.mullvad.mullvadvpn.compose.state.CustomListSheetUiState
import net.mullvad.mullvadvpn.lib.model.CustomListId
import net.mullvad.mullvadvpn.lib.model.CustomListName
import net.mullvad.mullvadvpn.viewmodel.CustomListSheetViewModel
import org.koin.androidx.compose.koinViewModel

data class CustomListSheetNavArgs(
val customListId: CustomListId,
val customListName: CustomListName
)

@OptIn(ExperimentalMaterial3Api::class)
@Destination<RootGraph>(
navArgs = CustomListSheetNavArgs::class,
style = DestinationStyleBottomSheet::class
)
@Composable
fun CustomListSheet(
navigator: DestinationsNavigator,
) {
val vm = koinViewModel<CustomListSheetViewModel>()
val state = vm.uiState.collectAsStateWithLifecycle()
MullvadModalBottomContainer {
CustomListContent(
state.value,
dropUnlessResumed {
navigator.navigate(
EditCustomListNameDestination(
state.value.customListId,
state.value.customListName
)
) {
popUpTo(CustomListSheetDestination) { inclusive = true }
}
},
dropUnlessResumed {
navigator.navigate(
CustomListLocationsDestination(state.value.customListId, false)
) {
popUpTo(CustomListSheetDestination) { inclusive = true }
}
},
dropUnlessResumed {
navigator.navigate(
DeleteCustomListDestination(
state.value.customListId,
state.value.customListName,
)
) {
popUpTo(CustomListSheetDestination) { inclusive = true }
}
},
)
}
}

@Composable
private fun ColumnScope.CustomListContent(
state: CustomListSheetUiState,
editCustomListName: () -> Unit,
editLocations: () -> Unit,
deleteCustomList: () -> Unit,
) {
HeaderCell(text = state.customListName.value, background = Color.Unspecified)
IconCell(
iconId = R.drawable.icon_edit,
title = stringResource(id = R.string.edit_name),
titleColor = MaterialTheme.colorScheme.onBackground,
onClick = editCustomListName,
background = Color.Unspecified
)
IconCell(
iconId = R.drawable.icon_add,
title = stringResource(id = R.string.edit_locations),
titleColor = MaterialTheme.colorScheme.onBackground,
onClick = editLocations,
background = Color.Unspecified
)
HorizontalDivider(color = MaterialTheme.colorScheme.onBackground)
IconCell(
iconId = R.drawable.icon_delete,
title = stringResource(id = R.string.delete),
titleColor = MaterialTheme.colorScheme.onBackground,
onClick = deleteCustomList,
background = Color.Unspecified
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
package net.mullvad.mullvadvpn.compose.bottomsheet

import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.compose.dropUnlessResumed
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.annotation.RootGraph
import com.ramcosta.composedestinations.bottomsheet.spec.DestinationStyleBottomSheet
import com.ramcosta.composedestinations.generated.destinations.ImportOverridesByTextDestination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.result.ResultBackNavigator
import net.mullvad.mullvadvpn.R
import net.mullvad.mullvadvpn.compose.cell.HeaderCell
import net.mullvad.mullvadvpn.compose.cell.IconCell
import net.mullvad.mullvadvpn.compose.component.MullvadModalBottomContainer
import net.mullvad.mullvadvpn.compose.test.SERVER_IP_OVERRIDES_IMPORT_BY_FILE_TEST_TAG
import net.mullvad.mullvadvpn.compose.test.SERVER_IP_OVERRIDES_IMPORT_BY_TEXT_TEST_TAG
import net.mullvad.mullvadvpn.lib.theme.Dimens
import net.mullvad.mullvadvpn.viewmodel.ImportOverridesSheetViewModel
import org.koin.androidx.compose.koinViewModel

@OptIn(ExperimentalMaterial3Api::class)
@Destination<RootGraph>(style = DestinationStyleBottomSheet::class)
@Composable
fun ImportOverridesSheet(
navigator: DestinationsNavigator,
resultRecipient: ResultBackNavigator<Boolean>
) {
val vm = koinViewModel<ImportOverridesSheetViewModel>()
val state = vm.uiState.collectAsStateWithLifecycle()

MullvadModalBottomContainer {
HeaderCell(
text = stringResource(id = R.string.server_ip_overrides_import_by),
background = Color.Unspecified
)
HorizontalDivider(color = MaterialTheme.colorScheme.onBackground)
IconCell(
iconId = R.drawable.icon_upload_file,
title = stringResource(id = R.string.server_ip_overrides_import_by_file),
onClick = dropUnlessResumed { resultRecipient.navigateBack(true) },
background = Color.Unspecified,
modifier = Modifier.testTag(SERVER_IP_OVERRIDES_IMPORT_BY_FILE_TEST_TAG)
)
IconCell(
iconId = R.drawable.icon_text_fields,
title = stringResource(id = R.string.server_ip_overrides_import_by_text),
onClick = dropUnlessResumed { navigator.navigate(ImportOverridesByTextDestination) },
background = Color.Unspecified,
modifier = Modifier.testTag(SERVER_IP_OVERRIDES_IMPORT_BY_TEXT_TEST_TAG)
)
if (state.value.overridesActive) {
HorizontalDivider(color = MaterialTheme.colorScheme.onBackground)
Row(
verticalAlignment = Alignment.CenterVertically,
) {
Icon(
modifier = Modifier.padding(Dimens.mediumPadding),
painter = painterResource(id = R.drawable.icon_info),
tint = MaterialTheme.colorScheme.errorContainer,
contentDescription = null
)
Text(
modifier =
Modifier.padding(
top = Dimens.smallPadding,
end = Dimens.mediumPadding,
bottom = Dimens.smallPadding
),
text = stringResource(R.string.import_overrides_bottom_sheet_override_warning),
maxLines = 2,
style = MaterialTheme.typography.bodySmall,
overflow = TextOverflow.Ellipsis,
)
}
}
}
}
Loading

0 comments on commit 0f6a02c

Please sign in to comment.