Skip to content
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

Settings screen #50

Merged
merged 25 commits into from
Nov 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
67a9944
Create Settings screen's skeleton
nicolegeorgieva Nov 25, 2024
6a77ee9
Remove not needed imports
nicolegeorgieva Nov 25, 2024
bcd8d4b
Remove unused code
nicolegeorgieva Nov 25, 2024
7ce784f
Refactoring
nicolegeorgieva Nov 25, 2024
45323ad
WIP: Implement SettingsContent
nicolegeorgieva Nov 26, 2024
1299573
WIP: Implement SettingsContent - add buttons
nicolegeorgieva Nov 26, 2024
4cbbc82
WIP: Implement SettingsContent - create events and wire them
nicolegeorgieva Nov 26, 2024
681250b
WIP: Implement SettingsContent - use MaterialTheme error color; fix b…
nicolegeorgieva Nov 26, 2024
c800202
Navigate to SettingsScreen from Home
nicolegeorgieva Nov 26, 2024
b3382d3
Improve UI
nicolegeorgieva Nov 26, 2024
b7d77b1
WIP: Implement IvyButton
nicolegeorgieva Nov 27, 2024
fe88fe5
Fix SettingsScreen's routing
nicolegeorgieva Nov 28, 2024
3bcb0b1
Add DarkGray color
nicolegeorgieva Nov 28, 2024
e176ee4
Improve modeling of button
nicolegeorgieva Nov 28, 2024
900ce7c
WIP: IvyButton
nicolegeorgieva Nov 28, 2024
50441d4
WIP: IvyButton
nicolegeorgieva Nov 28, 2024
be9f54f
Implement IvyButton
nicolegeorgieva Nov 28, 2024
13593e1
Create new state and events for deleting account
nicolegeorgieva Nov 28, 2024
ab21e77
Improve SettingsContent alignment
nicolegeorgieva Nov 28, 2024
c9bd168
Migrate SettingsContent to use IvyButton; refactoring
nicolegeorgieva Nov 28, 2024
30a06da
Migrate SettingsContent to use IvyButton; refactoring
nicolegeorgieva Nov 28, 2024
b8f7b82
Fix build error
nicolegeorgieva Nov 28, 2024
d951968
Refactoring
nicolegeorgieva Nov 28, 2024
c2b390a
Refactoring
nicolegeorgieva Nov 28, 2024
9ba0d49
Improve UI
nicolegeorgieva Nov 28, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions composeApp/src/commonMain/kotlin/component/LearnScaffold.kt
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ fun LearnScaffold(
backButton: BackButton?,
title: String,
modifier: Modifier = Modifier,
actions: @Composable (Modifier) -> Unit = {},
bottomBar: @Composable () -> Unit = {},
floatingActionButton: @Composable () -> Unit = {},
topBarCenterContent: (@Composable () -> Unit)? = null,
Expand All @@ -39,6 +40,7 @@ fun LearnScaffold(
backButton = backButton,
title = title,
centerContent = topBarCenterContent,
actions = actions
)
},
bottomBar = bottomBar,
Expand Down
214 changes: 214 additions & 0 deletions composeApp/src/commonMain/kotlin/component/button/IvyButton.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,214 @@
package component.button

import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.RowScope
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.width
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp
import ui.theme.Gray
import ui.theme.colorsExt

sealed interface ButtonAppearance {
val style: ButtonStyle

data class Filled(override val style: ButtonStyle) : ButtonAppearance
data class Outlined(override val style: ButtonStyle) : ButtonAppearance
data class Text(override val style: ButtonStyle) : ButtonAppearance
}

sealed interface ButtonStyle {
data object Primary : ButtonStyle
data object Secondary : ButtonStyle
data object Neutral : ButtonStyle
data object Destructive : ButtonStyle
}

@Composable
fun IvyButton(
appearance: ButtonAppearance,
modifier: Modifier = Modifier,
enabled: Boolean = true,
text: @Composable (RowScope.() -> Unit)? = null,
icon: @Composable (RowScope.() -> Unit)? = null,
iconRight: @Composable (RowScope.() -> Unit)? = null,
onClick: () -> Unit,
) {
ButtonWrapper(
appearance = appearance,
modifier = modifier,
enabled = enabled,
onClick = onClick
) {
when {
icon != null && text != null -> {
icon()
Spacer(Modifier.width(8.dp))
text()
}

text != null && iconRight != null -> {
text()
Spacer(Modifier.width(8.dp))
iconRight()
}

icon != null -> {
icon()
}

text != null -> {
text()
}
}
}
}

@Composable
private fun ButtonWrapper(
appearance: ButtonAppearance,
modifier: Modifier = Modifier,
enabled: Boolean = true,
onClick: () -> Unit,
content: @Composable (RowScope.() -> Unit)
) {
val contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp)
val colors = appearance.buttonColors()

when (appearance) {
is ButtonAppearance.Filled -> {
Button(
modifier = modifier,
enabled = enabled,
contentPadding = contentPadding,
colors = colors,
onClick = onClick,
content = content
)
}

is ButtonAppearance.Outlined -> {
OutlinedButton(
modifier = modifier,
enabled = enabled,
contentPadding = contentPadding,
colors = colors,
border = BorderStroke(
width = 1.dp,
color = appearance.style.outlinedBorderColor()
),
onClick = onClick,
content = content
)
}

is ButtonAppearance.Text -> {
TextButton(
modifier = modifier,
enabled = enabled,
contentPadding = contentPadding,
colors = colors,
onClick = onClick,
content = content
)
}
}
}

@Composable
private fun ButtonAppearance.buttonColors(): ButtonColors {
return when (this) {
is ButtonAppearance.Filled -> this.colors()
is ButtonAppearance.Outlined -> this.colors()
is ButtonAppearance.Text -> this.colors()
}
}

@Composable
private fun ButtonAppearance.Filled.colors(): ButtonColors {
return when (this.style) {
ButtonStyle.Primary -> ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.primary,
contentColor = MaterialTheme.colors.onPrimary
)

ButtonStyle.Secondary -> ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.secondary,
contentColor = MaterialTheme.colors.onSecondary
)

ButtonStyle.Neutral -> ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colorsExt.backgroundVariant,
contentColor = MaterialTheme.colorsExt.onBackgroundVariant
)

ButtonStyle.Destructive -> ButtonDefaults.buttonColors(
backgroundColor = MaterialTheme.colors.error,
contentColor = MaterialTheme.colors.onError
)
}
}

@Composable
private fun ButtonAppearance.Outlined.colors(): ButtonColors {
return when (this.style) {
ButtonStyle.Primary -> ButtonDefaults.outlinedButtonColors(
backgroundColor = Color.Transparent,
contentColor = MaterialTheme.colors.primary
)

ButtonStyle.Secondary -> ButtonDefaults.outlinedButtonColors(
backgroundColor = Color.Transparent,
contentColor = MaterialTheme.colors.secondary
)

ButtonStyle.Neutral -> ButtonDefaults.outlinedButtonColors(
backgroundColor = Color.Transparent,
contentColor = Gray
)

ButtonStyle.Destructive -> ButtonDefaults.outlinedButtonColors(
backgroundColor = Color.Transparent,
contentColor = MaterialTheme.colors.error
)
}
}

@Composable
private fun ButtonAppearance.Text.colors(): ButtonColors {
return when (this.style) {
ButtonStyle.Primary -> ButtonDefaults.textButtonColors(
backgroundColor = Color.Transparent,
contentColor = MaterialTheme.colors.primary
)

ButtonStyle.Secondary -> ButtonDefaults.textButtonColors(
backgroundColor = Color.Transparent,
contentColor = MaterialTheme.colors.secondary
)

ButtonStyle.Neutral -> ButtonDefaults.textButtonColors(
backgroundColor = Color.Transparent,
contentColor = Gray
)

ButtonStyle.Destructive -> ButtonDefaults.textButtonColors(
backgroundColor = Color.Transparent,
contentColor = MaterialTheme.colors.error
)
}
}

@Composable
private fun ButtonStyle.outlinedBorderColor(): Color {
return when (this) {
ButtonStyle.Primary -> MaterialTheme.colors.primary
ButtonStyle.Secondary -> MaterialTheme.colors.secondary
ButtonStyle.Neutral -> Gray
ButtonStyle.Destructive -> MaterialTheme.colors.error
}
}
2 changes: 2 additions & 0 deletions composeApp/src/commonMain/kotlin/navigation/Routing.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,15 @@ import ui.screen.course.CourseRouter
import ui.screen.home.HomeRouter
import ui.screen.intro.IntroRouter
import ui.screen.lesson.LessonRouter
import ui.screen.settings.SettingsRouter

object Routing {
private val routers = setOf<Router<*>>(
IntroRouter,
HomeRouter,
LessonRouter,
CourseRouter,
SettingsRouter
)

fun resolve(route: Route): Screen? {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import navigation.Navigation
import ui.ComposeViewModel
import ui.screen.course.CourseScreen
import ui.screen.home.mapper.HomeViewStateMapper
import ui.screen.settings.SettingsScreen

class HomeViewModel(
private val navigation: Navigation,
Expand Down Expand Up @@ -36,13 +37,18 @@ class HomeViewModel(
when (event) {
HomeViewEvent.OnBackClick -> handleBackClick()
is HomeViewEvent.OnCourseClick -> handleCourseClick(event)
HomeViewEvent.OnSettingsClick -> handleSettingsClick()
}
}

private fun handleBackClick() {
navigation.navigateBack()
}

private fun handleSettingsClick() {
navigation.navigateTo(SettingsScreen())
}

private fun handleCourseClick(event: HomeViewEvent.OnCourseClick) {
navigation.navigateTo(CourseScreen(courseId = event.id, courseName = event.name))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,6 @@ sealed interface HomeViewEvent {
val id: CourseId,
val name: String,
) : HomeViewEvent

data object OnSettingsClick : HomeViewEvent
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,13 @@ import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.GridItemSpan
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.lazy.grid.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Settings
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import component.*
import component.button.SecondaryButton
import ivy.model.CourseId
import ui.screen.home.HomeItemViewState
import ui.screen.home.HomeViewEvent
Expand All @@ -27,7 +30,17 @@ fun HomeContent(
onEvent(HomeViewEvent.OnBackClick)
}
),
title = "Learn"
title = "Learn",
actions = {
// TODO - update button
SecondaryButton(
text = "",
icon = Icons.Filled.Settings,
onClick = {
onEvent(HomeViewEvent.OnSettingsClick)
}
)
}
) { contentPadding ->
val columnsCount = when (screenType()) {
ScreenType.Mobile -> 1
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package ui.screen.settings

import androidx.compose.runtime.Composable
import arrow.core.Option
import arrow.core.raise.option
import ivy.di.Di
import ivy.di.autowire.autoWire
import navigation.Route
import navigation.Router
import navigation.Screen
import ui.screen.settings.composable.SettingsContent

object SettingsRouter : Router<SettingsScreen> {
const val PATH = "settings"

override fun fromRoute(route: Route): Option<SettingsScreen> = option {
ensure(route.path == PATH)
SettingsScreen()
}

override fun toRoute(screen: SettingsScreen): Route {
return Route(path = PATH)
}
}

class SettingsScreen : Screen() {
override fun toRoute(): Route = SettingsRouter.toRoute(this)

override fun Di.Scope.onDi() {
autoWire(::SettingsViewModel)
}

private val viewModel: SettingsViewModel by lazy { Di.get() }

@Composable
override fun Content() {
SettingsContent(
viewState = viewModel.viewState(),
onEvent = viewModel::onEvent
)
}
}
Loading