Skip to content

Commit

Permalink
First lesson (#31)
Browse files Browse the repository at this point in the history
* Update intro button copy

* Make the PrimaryButton bigger

* Make the CTA bar smaller

* Style the Lesson screen

* Add validation for lesson items ids

* WIP: Lesson 1

* Improve UI scaling

* Improve UI

* Auto scroll to last item

* Add platform capabilities to play sound

* Add support for sound items

* Add play icon
  • Loading branch information
ILIYANGERMANOV committed May 31, 2024
1 parent 34d89f0 commit d4533c9
Show file tree
Hide file tree
Showing 33 changed files with 290 additions and 110 deletions.
15 changes: 15 additions & 0 deletions composeApp/src/commonMain/kotlin/component/ComposeUtils.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package component

import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalWindowInfo
Expand All @@ -18,6 +19,20 @@ fun screenWidth(): Dp {
return with(density) { containerSize.width.toDp() }
}

@Composable
fun screenType(): ScreenType = when {
screenWidth() < 600.dp -> ScreenType.Mobile
screenWidth() < 1200.dp -> ScreenType.Tablet
else -> ScreenType.Desktop
}

@Immutable
enum class ScreenType {
Mobile,
Tablet,
Desktop
}

@Composable
fun platformHorizontalPadding(): Dp {
return if (isLargeScreen()) 24.dp else 16.dp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ fun PrimaryButton(
Button(
modifier = modifier,
enabled = enabled,
contentPadding = PaddingValues(horizontal = 12.dp, vertical = 8.dp),
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
onClick = onClick
) {
Text(text = text)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package component.button

import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.width
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.OutlinedButton
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.unit.dp

@Composable
fun SecondaryButton(
text: String,
modifier: Modifier = Modifier,
icon: ImageVector? = null,
enabled: Boolean = true,
onClick: () -> Unit,
) {
OutlinedButton(
modifier = modifier,
enabled = enabled,
border = BorderStroke(1.dp, MaterialTheme.colors.secondary),
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp),
onClick = onClick
) {
if (icon != null) {
Icon(imageVector = icon, contentDescription = null)
Spacer(Modifier.width(8.dp))
}
Text(text = text)
}
}
15 changes: 15 additions & 0 deletions composeApp/src/commonMain/kotlin/component/text/Headline.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,19 @@ fun Headline(
color = color,
fontWeight = FontWeight.Black,
)
}

@Composable
fun HeadlineSmall(
text: String,
modifier: Modifier = Modifier,
color: Color = MaterialTheme.colors.onBackground,
) {
Text(
modifier = modifier,
text = text,
style = MaterialTheme.typography.h5,
color = color,
fontWeight = FontWeight.Black,
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import component.ScreenType.*
import component.isLargeScreen
import component.screenType
import component.text.SubTitle
import component.text.Title
import io.kamel.image.KamelImage
Expand Down Expand Up @@ -67,7 +69,13 @@ private fun Banner(
KamelImage(
modifier = modifier
.fillMaxWidth()
.aspectRatio(if (isLargeScreen()) 4f else 3.5f),
.aspectRatio(
when (screenType()) {
Mobile -> 3.5f
Tablet -> 4f
Desktop -> 5f
}
),
contentScale = ContentScale.Crop,
resource = asyncPainterResource(imageUrl),
contentDescription = null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,10 @@ fun HomeContent(
),
title = "Learn"
) { contentPadding ->
val columnsCount = when (screenWidth().value) {
in 0f..600f -> 1
in 600f..1200f -> 2
else -> 3
val columnsCount = when (screenType()) {
ScreenType.Mobile -> 1
ScreenType.Tablet -> 2
ScreenType.Desktop -> 3
}
val horizontalPadding = platformHorizontalPadding()
LazyVerticalGrid(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,6 @@
package ui.screen.intro.composable

import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.*
import androidx.compose.material.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
Expand Down Expand Up @@ -60,7 +54,7 @@ private fun ContinueButton(
) {
CtaButton(
modifier = modifier,
text = "LET'S GO",
text = "LET'S GO!",
onClick = onClick
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import ui.navigation.Screen
import ui.screen.lesson.composable.LessonContent
import ui.screen.lesson.handler.OnBackClickEventHandler
import ui.screen.lesson.handler.OnContinueClickEventHandler
import ui.screen.lesson.handler.OnSoundClickEventHandler
import ui.screen.lesson.handler.QuestionViewEventHandler
import ui.screen.lesson.mapper.LessonTreeManager
import ui.screen.lesson.mapper.LessonViewStateMapper
Expand All @@ -26,6 +27,7 @@ class LessonScreen(
register { OnBackClickEventHandler(Di.get()) }
register { OnContinueClickEventHandler() }
register { QuestionViewEventHandler() }
register { OnSoundClickEventHandler(Di.get()) }
register {
LessonViewModel(
courseId = courseId,
Expand All @@ -38,6 +40,7 @@ class LessonScreen(
Di.get<OnBackClickEventHandler>(),
Di.get<OnContinueClickEventHandler>(),
Di.get<QuestionViewEventHandler>(),
Di.get<OnSoundClickEventHandler>()
)
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,9 +117,17 @@ data class MysteryItemViewState(
@Immutable
value class LessonItemIdViewState(val value: String)

@Immutable
data class SoundItemViewState(
override val id: LessonItemIdViewState,
val text: String,
val soundUrl: String
) : LessonItemViewState

sealed interface LessonViewEvent {
data object OnBackClick : LessonViewEvent
data class OnContinueClick(val currentItemId: LessonItemIdViewState) : LessonViewEvent
data class OnSoundClick(val soundUrl: String) : LessonViewEvent
}

sealed interface QuestionViewEvent : LessonViewEvent {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ fun CtaBar(
Box(
modifier = modifier.fillMaxWidth()
.background(MaterialTheme.colors.background)
.padding(bottom = 16.dp, top = 12.dp),
.padding(vertical = 8.dp),
contentAlignment = Alignment.Center,
) {
when (viewState) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
Expand All @@ -16,6 +18,7 @@ import component.platformHorizontalPadding
import ui.screen.lesson.*
import ui.screen.lesson.composable.item.ImageLessonItem
import ui.screen.lesson.composable.item.QuestionLessonItem
import ui.screen.lesson.composable.item.SoundLessonItem
import ui.screen.lesson.composable.item.TextLessonItem

@Composable
Expand Down Expand Up @@ -57,9 +60,18 @@ private fun LessonItemsLazyColum(
modifier: Modifier = Modifier,
) {
val horizontalPadding = platformHorizontalPadding()
val listState = rememberLazyListState()

LaunchedEffect(viewState.items.size) {
if (viewState.items.size > 1) {
listState.animateScrollToItem(viewState.items.lastIndex)
}
}

LazyColumn(
modifier = modifier
.fillMaxSize(),
state = listState,
horizontalAlignment = Alignment.CenterHorizontally,
contentPadding = PaddingValues(
bottom = 96.dp,
Expand Down Expand Up @@ -118,6 +130,13 @@ private fun LessonItemsLazyColum(
)

is TextItemViewState -> TextLessonItem(it)

is SoundItemViewState -> SoundLessonItem(
viewState = it,
onClick = { soundUrl ->
onEvent(LessonViewEvent.OnSoundClick(soundUrl))
}
)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package ui.screen.lesson.composable.item

import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.unit.dp
import io.kamel.image.KamelImage
import io.kamel.image.asyncPainterResource
Expand All @@ -14,7 +16,8 @@ fun ImageLessonItem(
modifier: Modifier = Modifier,
) {
KamelImage(
modifier = modifier.padding(top = 12.dp),
modifier = modifier.padding(top = 12.dp)
.clip(RoundedCornerShape(16.dp)),
resource = asyncPainterResource(viewState.imageUrl),
contentDescription = null
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ fun QuestionLessonItem(
)
}
if (!viewState.answered) {
Spacer(Modifier.height(12.dp))
Spacer(Modifier.height(8.dp))
CheckButton(
modifier = Modifier.align(Alignment.End),
enabled = viewState.answers.any { it.selected },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package ui.screen.lesson.composable.item

import androidx.compose.foundation.layout.padding
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import component.button.SecondaryButton
import ui.screen.lesson.SoundItemViewState

@Composable
fun SoundLessonItem(
viewState: SoundItemViewState,
onClick: (soundUrl: String) -> Unit,
modifier: Modifier = Modifier,
) {
SecondaryButton(
modifier = modifier.padding(top = 12.dp),
text = viewState.text,
icon = Icons.Default.PlayArrow,
onClick = { onClick(viewState.soundUrl) },
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import component.text.Body
import component.text.Headline
import component.text.HeadlineSmall
import ui.screen.lesson.TextItemViewState
import ui.screen.lesson.TextStyleViewState

Expand All @@ -15,7 +15,7 @@ fun TextLessonItem(
modifier: Modifier = Modifier
) {
when (viewState.style) {
TextStyleViewState.Heading -> Headline(
TextStyleViewState.Heading -> HeadlineSmall(
modifier = modifier.padding(top = 16.dp),
text = viewState.text
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package ui.screen.lesson.handler

import Platform
import ui.EventHandler
import ui.screen.lesson.LessonViewEvent
import ui.screen.lesson.LessonViewModel
import ui.screen.lesson.LessonVmContext

class OnSoundClickEventHandler(
private val platform: Platform
) : EventHandler<LessonViewEvent.OnSoundClick, LessonViewModel.LocalState> {
override val eventTypes = setOf(LessonViewEvent.OnSoundClick::class)

override suspend fun LessonVmContext.handleEvent(event: LessonViewEvent.OnSoundClick) {
platform.playSound(soundUrl = event.soundUrl)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ class LessonTreeManager {
currentItemId in localState.completed
}

is SoundItem -> true

else -> {
currentItemId in localState.completed || autoLoadNextN > 0
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class LessonViewStateMapper(
is OpenQuestionItem -> toViewState(localState)
is QuestionItem -> toViewState(localState)
is TextItem -> toViewState()
is SoundItem -> toViewState()
}

private fun ChoiceItem.toViewState(): ChoiceItemViewState = ChoiceItemViewState(
Expand Down Expand Up @@ -133,6 +134,12 @@ class LessonViewStateMapper(
Body -> TextStyleViewState.Body
}
)

private fun SoundItem.toViewState(): SoundItemViewState = SoundItemViewState(
id = id.toViewState(),
soundUrl = sound.url,
text = text,
)
}

fun LessonItemId.toViewState(): LessonItemIdViewState = LessonItemIdViewState(value)
Expand Down
Loading

0 comments on commit d4533c9

Please sign in to comment.