-
Notifications
You must be signed in to change notification settings - Fork 134
[WOOMOB-1023][Woo POS][Settings] Implement 2-pane layout with Android-style UI design #14459
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
kidinov
merged 15 commits into
trunk
from
woomob-1023-woo-possettings-2-pane-layout-based-on-the-wireframes
Aug 14, 2025
Merged
Changes from 12 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
12f2689
Add toolbar with back navigation to WooPosSettingsScreen
kidinov 622534b
Implement 2-pane settings architecture with string resources
kidinov 7117802
Refactor WooPosSettingsScreen to use WooPosSettingsViewModel and add …
kidinov 232f9c8
Some simplifications
kidinov ecd382b
Make category-destination relationship compile-time safe
kidinov c6fc1fc
Rename settings pane functions for consistency and update navigation …
kidinov c7f4cd6
Update POS settings UI to match Android settings design with icons, s…
kidinov 18d4171
Add scrolling, fix toolbar jumping, and improve detail animations in …
kidinov e836c24
Improve detail toolbar animations and fix vertical jumping
kidinov a73b68e
Update settings UI with Android-style selection
kidinov 4099fd0
Fix settings detail pane toolbar layout
kidinov e157201
Add ripple effect to hardware menu items
kidinov 6550edd
Use WooPosToolbar instead of the custom solution
kidinov 8bd41fb
Removed redundat riple declaration. Better looking clicking area
kidinov 60c66f9
Simplify clickable modifier by removing unnecessary interaction sourc…
kidinov File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
87 changes: 77 additions & 10 deletions
87
...mmerce/src/main/kotlin/com/woocommerce/android/ui/woopos/settings/WooPosSettingsScreen.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,28 +1,95 @@ | ||
| package com.woocommerce.android.ui.woopos.settings | ||
|
|
||
| import androidx.compose.foundation.layout.Box | ||
| import androidx.activity.compose.BackHandler | ||
| import androidx.compose.foundation.background | ||
| import androidx.compose.foundation.layout.Column | ||
| import androidx.compose.foundation.layout.Row | ||
| import androidx.compose.foundation.layout.fillMaxSize | ||
| import androidx.compose.foundation.layout.fillMaxWidth | ||
| import androidx.compose.foundation.layout.padding | ||
| import androidx.compose.foundation.layout.statusBarsPadding | ||
| import androidx.compose.material3.MaterialTheme | ||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.ui.Alignment | ||
| import androidx.compose.runtime.collectAsState | ||
| import androidx.compose.runtime.getValue | ||
| import androidx.compose.ui.Modifier | ||
| import androidx.compose.ui.res.stringResource | ||
| import androidx.compose.ui.text.font.FontWeight | ||
| import androidx.hilt.navigation.compose.hiltViewModel | ||
| import com.woocommerce.android.R | ||
| import com.woocommerce.android.ui.woopos.common.composeui.WooPosPreview | ||
| import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosText | ||
| import com.woocommerce.android.ui.woopos.common.composeui.designsystem.WooPosSpacing | ||
| import com.woocommerce.android.ui.woopos.common.composeui.designsystem.WooPosTheme | ||
| import com.woocommerce.android.ui.woopos.common.composeui.designsystem.WooPosTypography | ||
| import com.woocommerce.android.ui.woopos.root.navigation.WooPosNavigationEvent | ||
| import com.woocommerce.android.ui.woopos.settings.categories.WooPosSettingsCategoriesPaneScreen | ||
| import com.woocommerce.android.ui.woopos.settings.details.WooPosSettingsDetailPaneScreen | ||
|
|
||
| @Composable | ||
| @Suppress("UnusedParameter") | ||
| fun WooPosSettingsScreen( | ||
| onNavigationEvent: (WooPosNavigationEvent) -> Unit, | ||
| viewModel: WooPosSettingsViewModel = hiltViewModel() | ||
| ) { | ||
| Box( | ||
| modifier = Modifier.fillMaxSize(), | ||
| contentAlignment = Alignment.Center | ||
| val containerViewModel: WooPosSettingsViewModel = hiltViewModel() | ||
| val navigationState by containerViewModel.navigationState.collectAsState() | ||
|
|
||
| BackHandler { onNavigationEvent(WooPosNavigationEvent.GoBack) } | ||
|
|
||
| Row( | ||
| modifier = Modifier.fillMaxSize() | ||
| ) { | ||
| WooPosText( | ||
| text = "Settings Screen", | ||
| style = WooPosTypography.Heading, | ||
| Column( | ||
| modifier = Modifier | ||
| .weight(0.3f) | ||
| .background(MaterialTheme.colorScheme.surface) | ||
| ) { | ||
| SettingsCategoriesToolbar( | ||
| titleText = stringResource(R.string.woopos_settings_title) | ||
| ) | ||
|
|
||
| WooPosSettingsCategoriesPaneScreen( | ||
| selectedCategory = navigationState.selectedCategory, | ||
| onCategorySelected = containerViewModel::onCategorySelected, | ||
| modifier = Modifier.fillMaxSize() | ||
| ) | ||
| } | ||
|
|
||
| WooPosSettingsDetailPaneScreen( | ||
| state = navigationState, | ||
| onNavigate = containerViewModel::navigateToDetail, | ||
| onBack = containerViewModel::navigateBack, | ||
| modifier = Modifier | ||
| .weight(0.7f) | ||
| .background(MaterialTheme.colorScheme.surfaceContainerLow) | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| @Composable | ||
| private fun SettingsCategoriesToolbar( | ||
| titleText: String | ||
| ) { | ||
| WooPosText( | ||
| text = titleText, | ||
| style = WooPosTypography.Heading, | ||
| fontWeight = FontWeight.Bold, | ||
| maxLines = 1, | ||
| modifier = Modifier | ||
| .fillMaxWidth() | ||
| .statusBarsPadding() | ||
| .padding( | ||
| horizontal = WooPosSpacing.Medium.value, | ||
| vertical = WooPosSpacing.Medium.value | ||
| ) | ||
| ) | ||
| } | ||
|
|
||
| @WooPosPreview | ||
| @Composable | ||
| fun WooPosSettingsScreenPreview() { | ||
| WooPosTheme { | ||
| WooPosSettingsScreen( | ||
| onNavigationEvent = {} | ||
| ) | ||
| } | ||
| } |
48 changes: 48 additions & 0 deletions
48
...ommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/settings/WooPosSettingsState.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,48 @@ | ||
| package com.woocommerce.android.ui.woopos.settings | ||
|
|
||
| import androidx.annotation.StringRes | ||
| import com.woocommerce.android.R | ||
| import com.woocommerce.android.ui.woopos.settings.categories.WooPosSettingsCategory | ||
|
|
||
| data class WooPosSettingsState( | ||
| val selectedCategory: WooPosSettingsCategory = WooPosSettingsCategory.HARDWARE, | ||
| val currentDestination: WooPosSettingsDetailDestination = selectedCategory.rootDestination | ||
| ) { | ||
| val canGoBack: Boolean | ||
| get() = currentDestination.parentDestination != null | ||
| } | ||
|
|
||
| sealed class WooPosSettingsDetailDestination { | ||
| @get:StringRes | ||
| abstract val titleRes: Int | ||
| abstract val parentDestination: WooPosSettingsDetailDestination? | ||
| abstract val childDestinations: List<WooPosSettingsDetailDestination> | ||
|
|
||
| sealed class Hardware : WooPosSettingsDetailDestination() { | ||
| data object Overview : Hardware() { | ||
| override val titleRes: Int = R.string.woopos_settings_hardware_category | ||
| override val parentDestination: WooPosSettingsDetailDestination? = null | ||
| override val childDestinations: List<Hardware> = listOf(BarcodeScanners, CardReaders) | ||
| } | ||
|
|
||
| data object BarcodeScanners : Hardware() { | ||
| override val titleRes: Int = R.string.woopos_settings_hardware_barcode_scanners | ||
| override val parentDestination: Hardware = Overview | ||
| override val childDestinations: List<WooPosSettingsDetailDestination> = emptyList() | ||
| } | ||
|
|
||
| data object CardReaders : Hardware() { | ||
| override val titleRes: Int = R.string.woopos_settings_hardware_card_readers | ||
| override val parentDestination: Hardware = Overview | ||
| override val childDestinations: List<WooPosSettingsDetailDestination> = emptyList() | ||
| } | ||
| } | ||
|
|
||
| sealed class Store : WooPosSettingsDetailDestination() { | ||
| data object Overview : Store() { | ||
| override val titleRes: Int = R.string.woopos_settings_store_category | ||
| override val parentDestination: WooPosSettingsDetailDestination? = null | ||
| override val childDestinations: List<WooPosSettingsDetailDestination> = emptyList() | ||
| } | ||
| } | ||
| } | ||
36 changes: 35 additions & 1 deletion
36
...rce/src/main/kotlin/com/woocommerce/android/ui/woopos/settings/WooPosSettingsViewModel.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,8 +1,42 @@ | ||
| package com.woocommerce.android.ui.woopos.settings | ||
|
|
||
| import androidx.lifecycle.ViewModel | ||
| import com.woocommerce.android.ui.woopos.settings.categories.WooPosSettingsCategory | ||
| import dagger.hilt.android.lifecycle.HiltViewModel | ||
| import kotlinx.coroutines.flow.MutableStateFlow | ||
| import kotlinx.coroutines.flow.StateFlow | ||
| import kotlinx.coroutines.flow.asStateFlow | ||
| import kotlinx.coroutines.flow.update | ||
| import javax.inject.Inject | ||
|
|
||
| @HiltViewModel | ||
| class WooPosSettingsViewModel @Inject constructor() : ViewModel() | ||
| class WooPosSettingsViewModel @Inject constructor() : ViewModel() { | ||
| private val _navigationState = MutableStateFlow(WooPosSettingsState()) | ||
| val navigationState: StateFlow<WooPosSettingsState> = _navigationState.asStateFlow() | ||
|
|
||
| fun onCategorySelected(category: WooPosSettingsCategory) { | ||
| _navigationState.update { currentState -> | ||
| currentState.copy( | ||
| selectedCategory = category, | ||
| currentDestination = category.rootDestination | ||
| ) | ||
| } | ||
| } | ||
|
|
||
| fun navigateToDetail(destination: WooPosSettingsDetailDestination) { | ||
| _navigationState.update { currentState -> | ||
| currentState.copy(currentDestination = destination) | ||
| } | ||
| } | ||
|
|
||
| fun navigateBack() { | ||
| _navigationState.update { currentState -> | ||
| val parentDestination = currentState.currentDestination.parentDestination | ||
| if (parentDestination != null) { | ||
| currentState.copy(currentDestination = parentDestination) | ||
| } else { | ||
| currentState | ||
| } | ||
| } | ||
| } | ||
| } |
107 changes: 107 additions & 0 deletions
107
...m/woocommerce/android/ui/woopos/settings/categories/WooPosSettingsCategoriesPaneScreen.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,107 @@ | ||
| package com.woocommerce.android.ui.woopos.settings.categories | ||
|
|
||
| import androidx.compose.foundation.background | ||
| import androidx.compose.foundation.clickable | ||
| import androidx.compose.foundation.layout.Arrangement | ||
| import androidx.compose.foundation.layout.Column | ||
| import androidx.compose.foundation.layout.Row | ||
| import androidx.compose.foundation.layout.fillMaxSize | ||
| import androidx.compose.foundation.layout.fillMaxWidth | ||
| import androidx.compose.foundation.layout.padding | ||
| import androidx.compose.foundation.layout.size | ||
| import androidx.compose.foundation.rememberScrollState | ||
| import androidx.compose.foundation.shape.RoundedCornerShape | ||
| import androidx.compose.foundation.verticalScroll | ||
| import androidx.compose.material3.Icon | ||
| import androidx.compose.material3.MaterialTheme | ||
| import androidx.compose.runtime.Composable | ||
| import androidx.compose.runtime.collectAsState | ||
| import androidx.compose.runtime.getValue | ||
| import androidx.compose.ui.Alignment | ||
| import androidx.compose.ui.Modifier | ||
| import androidx.compose.ui.draw.clip | ||
| import androidx.compose.ui.res.stringResource | ||
| import androidx.compose.ui.unit.dp | ||
| import androidx.hilt.navigation.compose.hiltViewModel | ||
| import com.woocommerce.android.ui.woopos.common.composeui.component.WooPosText | ||
| import com.woocommerce.android.ui.woopos.common.composeui.designsystem.WooPosCornerRadius | ||
| import com.woocommerce.android.ui.woopos.common.composeui.designsystem.WooPosSpacing | ||
| import com.woocommerce.android.ui.woopos.common.composeui.designsystem.WooPosTypography | ||
|
|
||
| @Composable | ||
| fun WooPosSettingsCategoriesPaneScreen( | ||
| selectedCategory: WooPosSettingsCategory, | ||
| onCategorySelected: (WooPosSettingsCategory) -> Unit, | ||
| modifier: Modifier = Modifier, | ||
| viewModel: WooPosSettingsCategoriesViewModel = hiltViewModel() | ||
| ) { | ||
| val state by viewModel.state.collectAsState() | ||
|
|
||
| Column( | ||
| modifier = modifier | ||
| .fillMaxSize() | ||
| .verticalScroll(rememberScrollState()) | ||
| ) { | ||
| state.categories.forEach { item -> | ||
| CategoryItem( | ||
| item = item, | ||
| isSelected = item == selectedCategory, | ||
| onClick = { | ||
| onCategorySelected(item) | ||
| }, | ||
| modifier = Modifier.padding(horizontal = WooPosSpacing.Medium.value) | ||
| ) | ||
| } | ||
| } | ||
| } | ||
|
|
||
| @Composable | ||
| private fun CategoryItem( | ||
| item: WooPosSettingsCategory, | ||
| isSelected: Boolean, | ||
| onClick: () -> Unit, | ||
| modifier: Modifier = Modifier | ||
| ) { | ||
| Row( | ||
| modifier = modifier | ||
| .fillMaxWidth() | ||
| .clip(RoundedCornerShape(WooPosCornerRadius.Large.value)) | ||
| .background( | ||
| if (isSelected) { | ||
| MaterialTheme.colorScheme.secondary.copy(alpha = 0.5f) | ||
| } else { | ||
| MaterialTheme.colorScheme.surface | ||
| } | ||
| ) | ||
| .clickable { onClick() } | ||
| .padding( | ||
| horizontal = WooPosSpacing.Medium.value, | ||
| vertical = WooPosSpacing.Medium.value | ||
| ), | ||
| verticalAlignment = Alignment.CenterVertically, | ||
| horizontalArrangement = Arrangement.Start | ||
| ) { | ||
| Icon( | ||
| imageVector = item.icon, | ||
| contentDescription = null, | ||
| tint = MaterialTheme.colorScheme.onSurface, | ||
| modifier = Modifier.size(28.dp) | ||
| ) | ||
|
|
||
| Column( | ||
| modifier = Modifier.padding(start = WooPosSpacing.Medium.value) | ||
| ) { | ||
| WooPosText( | ||
| text = stringResource(item.titleRes), | ||
| style = WooPosTypography.BodyLarge, | ||
| color = MaterialTheme.colorScheme.onSurface | ||
| ) | ||
| WooPosText( | ||
| text = stringResource(item.subtitleRes), | ||
| style = WooPosTypography.BodySmall, | ||
| color = MaterialTheme.colorScheme.onSurfaceVariant, | ||
| modifier = Modifier.padding(top = WooPosSpacing.XSmall.value) | ||
| ) | ||
| } | ||
| } | ||
| } |
33 changes: 33 additions & 0 deletions
33
...in/com/woocommerce/android/ui/woopos/settings/categories/WooPosSettingsCategoriesState.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| package com.woocommerce.android.ui.woopos.settings.categories | ||
|
|
||
| import androidx.annotation.StringRes | ||
| import androidx.compose.material.icons.Icons | ||
| import androidx.compose.material.icons.filled.Hardware | ||
| import androidx.compose.material.icons.filled.Store | ||
| import androidx.compose.ui.graphics.vector.ImageVector | ||
| import com.woocommerce.android.R | ||
| import com.woocommerce.android.ui.woopos.settings.WooPosSettingsDetailDestination | ||
|
|
||
| enum class WooPosSettingsCategory( | ||
| @StringRes val titleRes: Int, | ||
| @StringRes val subtitleRes: Int, | ||
| val icon: ImageVector, | ||
| val rootDestination: WooPosSettingsDetailDestination | ||
| ) { | ||
| HARDWARE( | ||
| R.string.woopos_settings_hardware_category, | ||
| R.string.woopos_settings_hardware_category_subtitle, | ||
| Icons.Default.Hardware, | ||
| WooPosSettingsDetailDestination.Hardware.Overview | ||
| ), | ||
| STORE( | ||
| R.string.woopos_settings_store_category, | ||
| R.string.woopos_settings_store_category_subtitle, | ||
| Icons.Default.Store, | ||
| WooPosSettingsDetailDestination.Store.Overview | ||
| ) | ||
| } | ||
|
|
||
| data class WooPosSettingsCategoriesState( | ||
| val categories: List<WooPosSettingsCategory> = WooPosSettingsCategory.entries | ||
| ) |
14 changes: 14 additions & 0 deletions
14
...om/woocommerce/android/ui/woopos/settings/categories/WooPosSettingsCategoriesViewModel.kt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package com.woocommerce.android.ui.woopos.settings.categories | ||
|
|
||
| import androidx.lifecycle.ViewModel | ||
| import dagger.hilt.android.lifecycle.HiltViewModel | ||
| import kotlinx.coroutines.flow.MutableStateFlow | ||
| import kotlinx.coroutines.flow.StateFlow | ||
| import kotlinx.coroutines.flow.asStateFlow | ||
| import javax.inject.Inject | ||
|
|
||
| @HiltViewModel | ||
| class WooPosSettingsCategoriesViewModel @Inject constructor() : ViewModel() { | ||
| private val _state = MutableStateFlow(WooPosSettingsCategoriesState()) | ||
| val state: StateFlow<WooPosSettingsCategoriesState> = _state.asStateFlow() | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default value for currentDestination references selectedCategory.rootDestination, but selectedCategory is also being initialized in the same constructor. This creates a circular dependency where currentDestination depends on selectedCategory's rootDestination, but selectedCategory is initialized with a default value at the same time. This should be explicitly set to WooPosSettingsCategory.HARDWARE.rootDestination to avoid potential initialization issues.