diff --git a/samples/tacos/src/main/kotlin/com/slack/circuit/tacos/OrderTacosCircuit.kt b/samples/tacos/src/main/kotlin/com/slack/circuit/tacos/OrderTacosCircuit.kt index 7ce009dd5..15a09ca8d 100644 --- a/samples/tacos/src/main/kotlin/com/slack/circuit/tacos/OrderTacosCircuit.kt +++ b/samples/tacos/src/main/kotlin/com/slack/circuit/tacos/OrderTacosCircuit.kt @@ -4,7 +4,6 @@ package com.slack.circuit.tacos import androidx.activity.compose.BackHandler import androidx.annotation.StringRes -import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box @@ -16,6 +15,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.ArrowForward import androidx.compose.material3.CenterAlignedTopAppBar +import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold @@ -30,7 +30,6 @@ import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip -import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.graphics.vector.rememberVectorPainter import androidx.compose.ui.res.stringResource @@ -108,14 +107,11 @@ internal class OrderTacosPresenter( private val toppingsProducer: OrderStep.StateProducer, private val confirmationProducer: OrderStep.StateProducer, private val summaryProducer: OrderStep.StateProducer, + private val initialStep: OrderStep = FillingsOrderStep, + private val initialOrderDetails: OrderDetails = OrderDetails() ) : Presenter { - @Composable override fun present(): OrderTacosScreen.State = presentInternal() - @Composable - internal fun presentInternal( - initialStep: OrderStep = FillingsOrderStep, - initialOrderDetails: OrderDetails = OrderDetails(), - ): OrderTacosScreen.State { + override fun present(): OrderTacosScreen.State { var currentStep by remember { mutableStateOf(initialStep) } var orderDetails by remember { mutableStateOf(initialOrderDetails) } var isNextEnabled by remember { mutableStateOf(false) } @@ -168,7 +164,7 @@ private fun processNavigation( onNavEvent: (OrderStep) -> Unit ) { val newIndex = currentStep.index + navEvent.indexModifier - onNavEvent(orderSteps.getOrElse(newIndex) { currentStep }) + onNavEvent(orderSteps[newIndex]) } private fun updateOrder( @@ -267,17 +263,9 @@ private fun NavigationButton( ) { if (!visible) return - val tintColour = - when (enabled) { - true -> MaterialTheme.colorScheme.onSurface - false -> MaterialTheme.colorScheme.outline - } - IconButton(modifier = modifier, enabled = enabled, onClick = onClick) { - Image( - modifier = Modifier, + Icon( painter = rememberVectorPainter(image = direction.icon), - colorFilter = ColorFilter.tint(tintColour), contentDescription = stringResource(direction.descriptionResId), ) } @@ -293,18 +281,24 @@ private fun OrderTotal( ) { if (!isVisible) return - val color = + val bdColor = when { - onConfirmationStep -> MaterialTheme.colorScheme.onTertiaryContainer - else -> MaterialTheme.colorScheme.tertiaryContainer + onConfirmationStep -> MaterialTheme.colorScheme.secondaryContainer + else -> MaterialTheme.colorScheme.primaryContainer } + val textColor = + when { + onConfirmationStep -> MaterialTheme.colorScheme.onSecondaryContainer + else -> MaterialTheme.colorScheme.onPrimaryContainer + } + var boxModifier = modifier .fillMaxWidth() - .defaultMinSize(minHeight = 28.dp) + .defaultMinSize(minHeight = 32.dp) .padding(horizontal = 4.dp) .clip(RoundedCornerShape(4.dp)) - .background(color) + .background(bdColor) if (onConfirmationStep) boxModifier = boxModifier.clickable(onClick = onClick) val label = @@ -316,13 +310,13 @@ private fun OrderTotal( Text( text = label, modifier = Modifier.align(Alignment.CenterStart).padding(start = 4.dp), - color = MaterialTheme.colorScheme.onPrimary, + color = textColor, fontWeight = FontWeight.Bold, ) Text( text = "$$orderCost", modifier = Modifier.align(Alignment.CenterEnd).padding(end = 4.dp), - color = MaterialTheme.colorScheme.onPrimary, + color = textColor, fontWeight = FontWeight.Bold, ) } diff --git a/samples/tacos/src/main/kotlin/com/slack/circuit/tacos/step/FillingsOrderStep.kt b/samples/tacos/src/main/kotlin/com/slack/circuit/tacos/step/FillingsOrderStep.kt index 989d9d95b..487260132 100644 --- a/samples/tacos/src/main/kotlin/com/slack/circuit/tacos/step/FillingsOrderStep.kt +++ b/samples/tacos/src/main/kotlin/com/slack/circuit/tacos/step/FillingsOrderStep.kt @@ -7,10 +7,10 @@ import androidx.compose.foundation.layout.Box 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.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.RadioButton import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -85,7 +85,7 @@ internal fun FillingsUi(state: FillingsOrderStep.State, modifier: Modifier = Mod @Composable private fun Loading(modifier: Modifier = Modifier) { Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - CircularProgressIndicator(color = MaterialTheme.colorScheme.onSurface) + CircularProgressIndicator() } } @@ -96,7 +96,7 @@ private fun FillingsList( ) { val sink = state.eventSink val scrollState = rememberScrollState() - Column(modifier = modifier.verticalScroll(scrollState)) { + Column(modifier = modifier.verticalScroll(scrollState).fillMaxWidth()) { Instructions(resId = R.string.fillings_step_instructions) state.list.forEach { ingredient -> Filling( diff --git a/samples/tacos/src/main/kotlin/com/slack/circuit/tacos/step/ToppingsOrderStep.kt b/samples/tacos/src/main/kotlin/com/slack/circuit/tacos/step/ToppingsOrderStep.kt index d3e8ff62d..a107d40d8 100644 --- a/samples/tacos/src/main/kotlin/com/slack/circuit/tacos/step/ToppingsOrderStep.kt +++ b/samples/tacos/src/main/kotlin/com/slack/circuit/tacos/step/ToppingsOrderStep.kt @@ -7,11 +7,11 @@ import androidx.compose.foundation.layout.Box 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.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material3.Checkbox import androidx.compose.material3.CircularProgressIndicator -import androidx.compose.material3.MaterialTheme import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.produceState @@ -112,7 +112,7 @@ internal fun ToppingsUi(state: ToppingsOrderStep.State, modifier: Modifier = Mod @Composable private fun Loading(modifier: Modifier = Modifier) { Box(modifier = modifier.fillMaxSize(), contentAlignment = Alignment.Center) { - CircularProgressIndicator(color = MaterialTheme.colorScheme.onSurface) + CircularProgressIndicator() } } @@ -123,7 +123,7 @@ private fun ToppingsList( ) { val sink = state.eventSink val scrollState = rememberScrollState() - Column(modifier = modifier.verticalScroll(scrollState)) { + Column(modifier = modifier.verticalScroll(scrollState).fillMaxWidth()) { Instructions(resId = R.string.toppings_step_instructions) state.list.forEach { ingredient -> Topping( diff --git a/samples/tacos/src/main/kotlin/com/slack/circuit/tacos/ui/theme/Color.kt b/samples/tacos/src/main/kotlin/com/slack/circuit/tacos/ui/theme/Color.kt index baf2ef077..e1fdc3fc3 100644 --- a/samples/tacos/src/main/kotlin/com/slack/circuit/tacos/ui/theme/Color.kt +++ b/samples/tacos/src/main/kotlin/com/slack/circuit/tacos/ui/theme/Color.kt @@ -8,16 +8,16 @@ import androidx.compose.ui.graphics.Color val md_theme_light_primary = Color(0xFF6750A4) val md_theme_light_onPrimary = Color(0xFFFFFFFF) -val md_theme_light_primaryContainer = Color(0xFFE9DDFF) -val md_theme_light_onPrimaryContainer = Color(0xFF22005D) +val md_theme_light_primaryContainer = Color(0xFF7E5260) +val md_theme_light_onPrimaryContainer = Color(0xFFFFFFFF) val md_theme_light_secondary = Color(0xFF625B71) val md_theme_light_onSecondary = Color(0xFFFFFFFF) -val md_theme_light_secondaryContainer = Color(0xFFE8DEF8) -val md_theme_light_onSecondaryContainer = Color(0xFF1E192B) +val md_theme_light_secondaryContainer = Color(0xFFBA1A1A) +val md_theme_light_onSecondaryContainer = Color(0xFFFFFFFF) val md_theme_light_tertiary = Color(0xFF7E5260) val md_theme_light_onTertiary = Color(0xFFFFFFFF) -val md_theme_light_tertiaryContainer = Color(0xFF7E5260) -val md_theme_light_onTertiaryContainer = Color(0xFFBA1A1A) +val md_theme_light_tertiaryContainer = Color(0xFFFFD9E3) +val md_theme_light_onTertiaryContainer = Color(0xFF31101D) val md_theme_light_error = Color(0xFFBA1A1A) val md_theme_light_errorContainer = Color(0xFFFFDAD6) val md_theme_light_onError = Color(0xFFFFFFFF) @@ -39,12 +39,12 @@ val md_theme_light_scrim = Color(0xFF000000) val md_theme_dark_primary = Color(0xFFCFBCFF) val md_theme_dark_onPrimary = Color(0xFF381E72) -val md_theme_dark_primaryContainer = Color(0xFF4F378A) -val md_theme_dark_onPrimaryContainer = Color(0xFFE9DDFF) +val md_theme_dark_primaryContainer = Color(0xFF7A294B) +val md_theme_dark_onPrimaryContainer = Color(0xFFFFFFFF) val md_theme_dark_secondary = Color(0xFFCBC2DB) val md_theme_dark_onSecondary = Color(0xFF332D41) -val md_theme_dark_secondaryContainer = Color(0xFF4A4458) -val md_theme_dark_onSecondaryContainer = Color(0xFFE8DEF8) +val md_theme_dark_secondaryContainer = Color(0xFF930009) +val md_theme_dark_onSecondaryContainer = Color(0xFFFFFFFF) val md_theme_dark_tertiary = Color(0xFFEFB8C8) val md_theme_dark_onTertiary = Color(0xFF4A2532) val md_theme_dark_tertiaryContainer = Color(0xFF633B48) diff --git a/samples/tacos/src/main/kotlin/com/slack/circuit/tacos/ui/theme/Theme.kt b/samples/tacos/src/main/kotlin/com/slack/circuit/tacos/ui/theme/Theme.kt index c8529d20b..1c2417415 100644 --- a/samples/tacos/src/main/kotlin/com/slack/circuit/tacos/ui/theme/Theme.kt +++ b/samples/tacos/src/main/kotlin/com/slack/circuit/tacos/ui/theme/Theme.kt @@ -8,7 +8,7 @@ import androidx.compose.material3.darkColorScheme import androidx.compose.material3.lightColorScheme import androidx.compose.runtime.Composable -private val LightColors = +private val LightMaterialColors = lightColorScheme( primary = md_theme_light_primary, onPrimary = md_theme_light_onPrimary, @@ -41,7 +41,7 @@ private val LightColors = scrim = md_theme_light_scrim, ) -private val DarkColors = +private val DarkMaterialColors = darkColorScheme( primary = md_theme_dark_primary, onPrimary = md_theme_dark_onPrimary, @@ -76,11 +76,11 @@ private val DarkColors = @Composable fun TacoTheme(useDarkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) { - val colors = + val materialColors = when { - useDarkTheme -> DarkColors - else -> LightColors + useDarkTheme -> DarkMaterialColors + else -> LightMaterialColors } - MaterialTheme(colorScheme = colors, content = content) + MaterialTheme(colorScheme = materialColors, content = content) } diff --git a/samples/tacos/src/test/kotlin/com/slack/circuit/tacos/OrderTacosPresenterTest.kt b/samples/tacos/src/test/kotlin/com/slack/circuit/tacos/OrderTacosPresenterTest.kt index 86550edfe..f0b795062 100644 --- a/samples/tacos/src/test/kotlin/com/slack/circuit/tacos/OrderTacosPresenterTest.kt +++ b/samples/tacos/src/test/kotlin/com/slack/circuit/tacos/OrderTacosPresenterTest.kt @@ -68,33 +68,16 @@ class OrderTacosPresenterTest { toppingsProducer = { _, _ -> ToppingsOrderStep.State.Loading }, confirmationProducer = { _, _ -> error("wrong step") }, summaryProducer = { _, _ -> error("wrong step") }, + initialStep = ToppingsOrderStep ) - moleculeFlow(RecompositionClock.Immediate) { presenter.presentInternal(ToppingsOrderStep) } + moleculeFlow(RecompositionClock.Immediate) { presenter.present() } .test { awaitItem().run { eventSink(OrderTacosScreen.Event.Previous) } assertThat(awaitItem().stepState).isEqualTo(FillingsOrderStep.State.Loading) } } - @Test - fun `present - do nothing if navigation event does not make sense`() = runTest { - val presenter = - OrderTacosPresenter( - fillingsProducer = { _, _ -> FillingsOrderStep.State.Loading }, - toppingsProducer = { _, _ -> ToppingsOrderStep.State.Loading }, - confirmationProducer = { _, _ -> error("wrong step") }, - summaryProducer = { _, _ -> error("wrong step") }, - ) - - presenter.test { - awaitItem().run { eventSink(OrderTacosScreen.Event.Previous) } - - // navigation invalid; should not recompose - expectNoEvents() - } - } - @Test fun `present - toggles next button based on OrderStep validation event`() = runTest { lateinit var sink: (OrderStep.Event) -> Unit @@ -162,9 +145,10 @@ class OrderTacosPresenterTest { }, confirmationProducer = { _, _ -> error("wrong step") }, summaryProducer = { _, _ -> error("wrong step") }, + initialStep = ToppingsOrderStep, ) - moleculeFlow(RecompositionClock.Immediate) { presenter.presentInternal(ToppingsOrderStep) } + moleculeFlow(RecompositionClock.Immediate) { presenter.present() } .test { awaitItem() assertThat(details).isEqualTo(OrderDetails()) @@ -194,9 +178,10 @@ class OrderTacosPresenterTest { sink = eventSink SummaryOrderStep.SummaryState {} }, + initialStep = SummaryOrderStep, ) - moleculeFlow(RecompositionClock.Immediate) { presenter.presentInternal(SummaryOrderStep) } + moleculeFlow(RecompositionClock.Immediate) { presenter.present() } .test { assertThat(awaitItem().stepState).isInstanceOf(SummaryOrderStep.SummaryState::class.java) @@ -222,11 +207,10 @@ class OrderTacosPresenterTest { toppingsProducer = { _, _ -> error("wrong step") }, confirmationProducer = { _, _ -> error("wrong step") }, summaryProducer = { _, _ -> error("wrong step") }, + initialOrderDetails = initialData, ) - moleculeFlow(RecompositionClock.Immediate) { - presenter.presentInternal(initialOrderDetails = initialData) - } + moleculeFlow(RecompositionClock.Immediate) { presenter.present() } .test { awaitItem().run { assertThat(orderCost).isEqualTo(expectedCost.toCurrencyString()) } } } }