diff --git a/.editorconfig b/.editorconfig
new file mode 100755
index 0000000..3f61fc2
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,4 @@
+[*.{kt,kts}]
+ij_kotlin_allow_trailing_comma = true
+ij_kotlin_allow_trailing_comma_on_call_site = true
+
diff --git a/core/designsystem/config/detekt/detekt.yml b/core/designsystem/config/detekt/detekt.yml
new file mode 100644
index 0000000..aba4110
--- /dev/null
+++ b/core/designsystem/config/detekt/detekt.yml
@@ -0,0 +1,33 @@
+# Exceptions for compose. See https://detekt.dev/docs/introduction/compose
+naming:
+ FunctionNaming:
+ functionPattern: '[a-zA-Z][a-zA-Z0-9]*'
+
+ TopLevelPropertyNaming:
+ constantPattern: '[A-Z][A-Za-z0-9]*'
+
+complexity:
+ LongParameterList:
+ ignoreAnnotated: ['Composable']
+ TooManyFunctions:
+ ignoreAnnotatedFunctions: ['Preview']
+
+style:
+ MagicNumber:
+ ignorePropertyDeclaration: true
+ ignoreCompanionObjectPropertyDeclaration: true
+ ignoreAnnotated: ['Composable']
+
+ UnusedPrivateMember:
+ ignoreAnnotated: ['Composable']
+
+# Deviations from defaults
+formatting:
+ TrailingCommaOnCallSite:
+ active: true
+ autoCorrect: true
+ useTrailingCommaOnCallSite: true
+ TrailingCommaOnDeclarationSite:
+ active: true
+ autoCorrect: true
+ useTrailingCommaOnDeclarationSite: true
\ No newline at end of file
diff --git a/core/designsystem/src/main/kotlin/dev/adriankuta/kahootquiz/core/designsystem/Color.kt b/core/designsystem/src/main/kotlin/dev/adriankuta/kahootquiz/core/designsystem/Color.kt
index a83aa14..0242dce 100644
--- a/core/designsystem/src/main/kotlin/dev/adriankuta/kahootquiz/core/designsystem/Color.kt
+++ b/core/designsystem/src/main/kotlin/dev/adriankuta/kahootquiz/core/designsystem/Color.kt
@@ -1,6 +1,15 @@
package dev.adriankuta.kahootquiz.core.designsystem
+import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.luminance
+
+@Composable
+fun contrastiveTo(color: Color): Color = if (color.luminance() < 0.5) {
+ Color.White
+} else {
+ Color.Black
+}
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
@@ -10,4 +19,11 @@ val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)
-val Grey = Color(0xFFFAFAFA)
\ No newline at end of file
+val Grey = Color(0xFFFAFAFA)
+val Pink = Color(0xFFFF99AA)
+val Red = Color(0xFFFF3355)
+val Red2 = Color(0xFFE21B3C)
+val Blue2 = Color(0xFF1368CE)
+val Yellow3 = Color(0xFFD89E00)
+val Green = Color(0xFF66BF39)
+val Green2 = Color(0xFF26890C)
diff --git a/core/designsystem/src/main/kotlin/dev/adriankuta/kahootquiz/core/designsystem/TextUtils.kt b/core/designsystem/src/main/kotlin/dev/adriankuta/kahootquiz/core/designsystem/TextUtils.kt
new file mode 100644
index 0000000..a15e7e4
--- /dev/null
+++ b/core/designsystem/src/main/kotlin/dev/adriankuta/kahootquiz/core/designsystem/TextUtils.kt
@@ -0,0 +1,48 @@
+package dev.adriankuta.kahootquiz.core.designsystem
+
+import android.graphics.Typeface
+import android.text.Spanned
+import android.text.style.ForegroundColorSpan
+import android.text.style.StyleSpan
+import android.text.style.UnderlineSpan
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.text.AnnotatedString
+import androidx.compose.ui.text.SpanStyle
+import androidx.compose.ui.text.buildAnnotatedString
+import androidx.compose.ui.text.font.FontStyle
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.style.TextDecoration
+
+fun Spanned.toAnnotatedString(): AnnotatedString = buildAnnotatedString {
+ val spanned = this@toAnnotatedString
+ append(spanned.toString())
+ getSpans(0, spanned.length, Any::class.java).forEach { span ->
+ val start = getSpanStart(span)
+ val end = getSpanEnd(span)
+ when (span) {
+ is StyleSpan -> when (span.style) {
+ Typeface.BOLD -> addStyle(SpanStyle(fontWeight = FontWeight.Bold), start, end)
+ Typeface.ITALIC -> addStyle(SpanStyle(fontStyle = FontStyle.Italic), start, end)
+ Typeface.BOLD_ITALIC -> addStyle(
+ SpanStyle(
+ fontWeight = FontWeight.Bold,
+ fontStyle = FontStyle.Italic,
+ ),
+ start, end,
+ )
+ }
+
+ is UnderlineSpan -> addStyle(
+ SpanStyle(textDecoration = TextDecoration.Underline),
+ start,
+ end,
+ )
+
+ is ForegroundColorSpan -> addStyle(
+ SpanStyle(color = Color(span.foregroundColor)),
+ start,
+ end,
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/core/designsystem/src/main/res/drawable/ic_circle.xml b/core/designsystem/src/main/res/drawable/ic_circle.xml
new file mode 100644
index 0000000..8790a7f
--- /dev/null
+++ b/core/designsystem/src/main/res/drawable/ic_circle.xml
@@ -0,0 +1,15 @@
+
+
+
+
diff --git a/core/designsystem/src/main/res/drawable/ic_correct.xml b/core/designsystem/src/main/res/drawable/ic_correct.xml
new file mode 100644
index 0000000..8eb3548
--- /dev/null
+++ b/core/designsystem/src/main/res/drawable/ic_correct.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
diff --git a/core/designsystem/src/main/res/drawable/ic_diamond.xml b/core/designsystem/src/main/res/drawable/ic_diamond.xml
new file mode 100644
index 0000000..114eca9
--- /dev/null
+++ b/core/designsystem/src/main/res/drawable/ic_diamond.xml
@@ -0,0 +1,15 @@
+
+
+
+
diff --git a/core/designsystem/src/main/res/drawable/ic_square.xml b/core/designsystem/src/main/res/drawable/ic_square.xml
new file mode 100644
index 0000000..8bd8e54
--- /dev/null
+++ b/core/designsystem/src/main/res/drawable/ic_square.xml
@@ -0,0 +1,15 @@
+
+
+
+
diff --git a/core/designsystem/src/main/res/drawable/ic_triangle.xml b/core/designsystem/src/main/res/drawable/ic_triangle.xml
new file mode 100644
index 0000000..cd19e6f
--- /dev/null
+++ b/core/designsystem/src/main/res/drawable/ic_triangle.xml
@@ -0,0 +1,15 @@
+
+
+
+
diff --git a/core/designsystem/src/main/res/drawable/ic_wrong.xml b/core/designsystem/src/main/res/drawable/ic_wrong.xml
new file mode 100644
index 0000000..91359ed
--- /dev/null
+++ b/core/designsystem/src/main/res/drawable/ic_wrong.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/core/network/src/main/kotlin/dev/adriankuta/kahootquiz/core/network/models/QuestionDtos.kt b/core/network/src/main/kotlin/dev/adriankuta/kahootquiz/core/network/models/QuestionDtos.kt
index f1d9e08..ee956f5 100644
--- a/core/network/src/main/kotlin/dev/adriankuta/kahootquiz/core/network/models/QuestionDtos.kt
+++ b/core/network/src/main/kotlin/dev/adriankuta/kahootquiz/core/network/models/QuestionDtos.kt
@@ -24,7 +24,7 @@ data class QuestionDto(
data class ChoiceDto(
val answer: String?,
- val correct: Boolean?,
+ val correct: Boolean,
val languageInfo: LanguageInfoDto?
)
diff --git a/domain/src/main/kotlin/dev/adriankuta/kahootquiz/domain/models/Choice.kt b/domain/src/main/kotlin/dev/adriankuta/kahootquiz/domain/models/Choice.kt
index 26c8536..0023607 100644
--- a/domain/src/main/kotlin/dev/adriankuta/kahootquiz/domain/models/Choice.kt
+++ b/domain/src/main/kotlin/dev/adriankuta/kahootquiz/domain/models/Choice.kt
@@ -2,6 +2,6 @@ package dev.adriankuta.kahootquiz.domain.models
data class Choice(
val answer: String?,
- val correct: Boolean?,
+ val correct: Boolean,
val languageInfo: LanguageInfo? = null
)
\ No newline at end of file
diff --git a/domain/src/main/kotlin/dev/adriankuta/kahootquiz/domain/models/Question.kt b/domain/src/main/kotlin/dev/adriankuta/kahootquiz/domain/models/Question.kt
index 2be76a2..ec8a595 100644
--- a/domain/src/main/kotlin/dev/adriankuta/kahootquiz/domain/models/Question.kt
+++ b/domain/src/main/kotlin/dev/adriankuta/kahootquiz/domain/models/Question.kt
@@ -12,12 +12,12 @@ data class Question(
val pointsMultiplier: Int?,
val choices: List?,
val layout: String? = null,
- val image: String,
+ val image: String? = null,
val imageMetadata: ImageMetadata?,
val resources: String? = null,
val video: Video? = null,
val questionFormat: Int? = null,
val languageInfo: LanguageInfo? = null,
val media: List? = null,
- val choiceRange: ChoiceRange? = null
+ val choiceRange: ChoiceRange? = null,
)
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index b7943af..bee7a25 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -1,7 +1,4 @@
[versions]
-coilCompose = "3.3.0"
-coilNetworkOkhttp = "3.3.0"
-retrofit = "3.0.0"
targetSdk = "36"
compileSdk = "36"
minSdk = "23"
@@ -23,6 +20,8 @@ animation = "1.9.0"
appUpdateKtx = "2.1.0"
appcompat = "1.7.1"
billing = "8.0.0"
+coilCompose = "3.3.0"
+coilNetworkOkhttp = "3.3.0"
coreTest = "1.7.0" # https://developer.android.com/jetpack/androidx/releases/test
datastorePreferences = "1.1.7" # https://developer.android.com/topic/libraries/architecture/datastore#preferences-datastore-dependencies
datetime = "0.7.1" # https://github.com/Kotlin/kotlinx-datetime/releases
@@ -48,6 +47,7 @@ material = "1.12.0"
materialIconsExtended = "1.7.8"
mockk = "1.14.5" # https://github.com/mockk/mockk/releases
playServicesAds = "24.5.0"
+retrofit = "3.0.0"
reviewKtx = "2.0.2"
room = "2.7.2"
secrets = "2.0.1"
diff --git a/ui/quiz/src/main/kotlin/dev/adriankuta/kahootquiz/ui/quiz/QuizScreen.kt b/ui/quiz/src/main/kotlin/dev/adriankuta/kahootquiz/ui/quiz/QuizScreen.kt
index 89529f4..eecec68 100644
--- a/ui/quiz/src/main/kotlin/dev/adriankuta/kahootquiz/ui/quiz/QuizScreen.kt
+++ b/ui/quiz/src/main/kotlin/dev/adriankuta/kahootquiz/ui/quiz/QuizScreen.kt
@@ -2,17 +2,24 @@ package dev.adriankuta.kahootquiz.ui.quiz
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.heightIn
+import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
+import androidx.compose.foundation.lazy.grid.itemsIndexed
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -26,11 +33,21 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
+import androidx.core.text.HtmlCompat
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import coil3.compose.AsyncImage
+import dev.adriankuta.kahootquiz.core.designsystem.Blue2
+import dev.adriankuta.kahootquiz.core.designsystem.Green
+import dev.adriankuta.kahootquiz.core.designsystem.Green2
import dev.adriankuta.kahootquiz.core.designsystem.Grey
import dev.adriankuta.kahootquiz.core.designsystem.KahootQuizTheme
+import dev.adriankuta.kahootquiz.core.designsystem.Pink
+import dev.adriankuta.kahootquiz.core.designsystem.Red
+import dev.adriankuta.kahootquiz.core.designsystem.Red2
+import dev.adriankuta.kahootquiz.core.designsystem.Yellow3
+import dev.adriankuta.kahootquiz.core.designsystem.contrastiveTo
+import dev.adriankuta.kahootquiz.core.designsystem.toAnnotatedString
import dev.adriankuta.kahootquiz.domain.models.Choice
import dev.adriankuta.kahootquiz.domain.models.Question
import kotlin.time.Duration.Companion.seconds
@@ -39,20 +56,22 @@ import dev.adriankuta.kahootquiz.core.designsystem.R as DesignR
@Composable
fun QuizScreen(
modifier: Modifier = Modifier,
- viewModel: QuizScreenViewModel = hiltViewModel()
+ viewModel: QuizScreenViewModel = hiltViewModel(),
) {
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
QuizScreen(
uiState = uiState,
- modifier = modifier.fillMaxSize()
+ onSelect = viewModel::onChoiceSelected,
+ modifier = modifier.fillMaxSize(),
)
}
@Composable
private fun QuizScreen(
uiState: QuizUiState,
+ onSelect: (Int) -> Unit,
modifier: Modifier = Modifier,
) {
Box(modifier.fillMaxSize()) {
@@ -60,23 +79,27 @@ private fun QuizScreen(
painter = painterResource(id = DesignR.drawable.bg_image),
contentDescription = null,
contentScale = ContentScale.Crop,
- modifier = Modifier.fillMaxSize()
+ modifier = Modifier.fillMaxSize(),
)
Column(
- modifier = Modifier.fillMaxSize()
+ modifier = Modifier
+ .fillMaxWidth(),
) {
Toolbar(
modifier = Modifier
.fillMaxWidth()
.height(72.dp)
- .padding(8.dp)
+ .padding(8.dp),
)
QuestionContent(
question = uiState.currentQuestion ?: return,
- modifier = Modifier.padding(horizontal = 8.dp)
+ modifier = Modifier.padding(horizontal = 8.dp),
)
+ Spacer(Modifier.height(8.dp))
Choices(
- choices = uiState.currentQuestion.choices ?: emptyList() // TODO remove empty list
+ choices = uiState.currentQuestion.choices ?: emptyList(), // TODO remove empty list
+ answer = uiState.answer,
+ onSelect = onSelect,
)
}
}
@@ -84,10 +107,10 @@ private fun QuizScreen(
@Composable
private fun Toolbar(
- modifier: Modifier = Modifier
+ modifier: Modifier = Modifier,
) {
Box(
- modifier = modifier
+ modifier = modifier,
) {
Text(
text = "2/24",
@@ -95,9 +118,9 @@ private fun Toolbar(
.align(Alignment.CenterStart)
.background(
color = Grey,
- shape = RoundedCornerShape(60.dp)
+ shape = RoundedCornerShape(60.dp),
)
- .padding(horizontal = 8.dp, vertical = 4.dp)
+ .padding(horizontal = 8.dp, vertical = 4.dp),
)
Row(
@@ -105,14 +128,14 @@ private fun Toolbar(
.align(Alignment.Center)
.background(
color = Grey,
- shape = RoundedCornerShape(60.dp)
+ shape = RoundedCornerShape(60.dp),
)
- .padding(horizontal = 8.dp, vertical = 4.dp)
+ .padding(horizontal = 8.dp, vertical = 4.dp),
) {
Image(
painter = painterResource(id = DesignR.drawable.ic_type),
contentDescription = "",
- modifier = Modifier.size(24.dp)
+ modifier = Modifier.size(24.dp),
)
Spacer(Modifier.width(4.dp))
Text(
@@ -128,38 +151,175 @@ private fun QuestionContent(
modifier: Modifier = Modifier,
) {
Column(
- modifier = modifier
+ modifier = modifier,
) {
AsyncImage(
model = question.image,
contentDescription = question.imageMetadata?.altText,
- contentScale = ContentScale.Crop,
+ contentScale = ContentScale.FillWidth,
modifier = Modifier
.fillMaxWidth()
- .height(200.dp)
+ .heightIn(min = 200.dp),
)
Spacer(Modifier.height(16.dp))
Text(
- text = question.question ?: "",
+ text = HtmlCompat.fromHtml(
+ question.question ?: "",
+ HtmlCompat.FROM_HTML_MODE_COMPACT,
+ ).toAnnotatedString(),
textAlign = TextAlign.Center,
modifier = Modifier
.fillMaxWidth()
.background(
color = Color.White,
- shape = RoundedCornerShape(4.dp)
+ shape = RoundedCornerShape(4.dp),
)
- .padding(horizontal = 8.dp, vertical = 16.dp)
+ .padding(horizontal = 8.dp, vertical = 16.dp),
)
}
}
@Composable
private fun Choices(
- choices: List
+ choices: List,
+ onSelect: (Int) -> Unit,
+ answer: AnswerUiState?,
+ modifier: Modifier = Modifier,
) {
- LazyVerticalGrid() { }
+ LazyVerticalGrid(
+ columns = GridCells.Fixed(2),
+ contentPadding = PaddingValues(8.dp),
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ verticalArrangement = Arrangement.spacedBy(8.dp),
+ modifier = modifier,
+ ) {
+ itemsIndexed(choices) { index, choice ->
+ ChoiceItem(
+ choice = choice,
+ index = index,
+ answer = answer,
+ onClick = { onSelect(index) },
+ )
+ }
+ }
}
+@Composable
+private fun ChoiceItem(
+ choice: Choice,
+ onClick: () -> Unit,
+ index: Int,
+ answer: AnswerUiState?,
+) {
+ if (answer != null) {
+ ChoiceItemRevealed(
+ choice = choice,
+ index = index,
+ isSelected = answer.selectedChoiceIndex == index,
+ )
+ } else {
+ ChoiceItemDefault(
+ choice = choice,
+ index = index,
+ onClick = onClick,
+ )
+ }
+}
+
+@Composable
+private fun ChoiceItemDefault(
+ choice: Choice,
+ index: Int,
+ onClick: () -> Unit,
+) {
+ val backgroundColor = when (index) {
+ 0 -> Red2
+ 1 -> Blue2
+ 2 -> Yellow3
+ 3 -> Green2
+ else -> Color.Gray
+ }
+
+ // TODO Add icons
+ val icon = when (index) {
+ 0 -> DesignR.drawable.ic_triangle
+ 1 -> DesignR.drawable.ic_diamond
+ 2 -> DesignR.drawable.ic_circle
+ else -> DesignR.drawable.ic_square
+ }
+ Box(
+ modifier = Modifier
+ .background(backgroundColor, shape = RoundedCornerShape(4.dp))
+ .height(100.dp)
+ .clickable(
+ onClick = onClick,
+ ),
+ ) {
+ Image(
+ painter = painterResource(id = icon),
+ contentDescription = null,
+ modifier = Modifier
+ .padding(8.dp)
+ .size(32.dp),
+ )
+ Text(
+ text = choice.answer ?: "",
+ textAlign = TextAlign.Center,
+ modifier = Modifier.align(Alignment.Center),
+ color = contrastiveTo(backgroundColor),
+ )
+ }
+}
+
+@Composable
+private fun ChoiceItemRevealed(
+ choice: Choice,
+ index: Int,
+ isSelected: Boolean,
+) {
+ val backgroundColor = when {
+ isSelected && !choice.correct -> Red
+ choice.correct -> Green
+ else -> Pink
+ }
+
+ val icon = if (choice.correct) {
+ DesignR.drawable.ic_correct
+ } else {
+ DesignR.drawable.ic_wrong
+ }
+
+ val alignment = if (index % 2 == 0) {
+ Alignment.TopStart
+ } else {
+ Alignment.TopEnd
+ }
+
+ Box(
+ modifier = Modifier
+ .background(backgroundColor, shape = RoundedCornerShape(4.dp))
+ .height(100.dp),
+ ) {
+ Image(
+ painter = painterResource(icon),
+ contentDescription = null,
+ modifier = Modifier
+ .align(alignment)
+ .offset(
+ x = if (alignment == Alignment.TopStart) (-8).dp else (8).dp,
+ (-8).dp,
+ ),
+ )
+ Text(
+ text = choice.answer ?: "",
+ textAlign = TextAlign.Center,
+ modifier = Modifier.align(Alignment.Center),
+ color = contrastiveTo(backgroundColor),
+ )
+ }
+}
+
+
@Preview
@Composable
private fun QuizScreenPreview() {
@@ -172,7 +332,7 @@ private fun QuizScreenPreview() {
Choice(answer = "Berlin", correct = false),
Choice(answer = "Madrid", correct = false),
Choice(answer = "Paris", correct = true),
- Choice(answer = "Rome", correct = false)
+ Choice(answer = "Rome", correct = false),
),
pointsMultiplier = 1,
time = 30.seconds,
@@ -180,7 +340,41 @@ private fun QuizScreenPreview() {
imageMetadata = null,
)
QuizScreen(
- uiState = QuizUiState(currentQuestion = sampleQuestion)
+ uiState = QuizUiState(
+ currentQuestion = sampleQuestion,
+ ),
+ onSelect = {},
+ )
+ }
+}
+
+@Preview
+@Composable
+private fun QuizScreenRevealedAnswerPreview() {
+ KahootQuizTheme {
+ val sampleQuestion = Question(
+ type = "quiz",
+ image = "", // Add a sample image URL or leave empty
+ question = "What is the capital of France?",
+ choices = listOf(
+ Choice(answer = "Berlin", correct = false),
+ Choice(answer = "Madrid", correct = false),
+ Choice(answer = "Paris", correct = true),
+ Choice(answer = "Rome", correct = false),
+ ),
+ pointsMultiplier = 1,
+ time = 30.seconds,
+ questionFormat = 0,
+ imageMetadata = null,
+ )
+ QuizScreen(
+ uiState = QuizUiState(
+ currentQuestion = sampleQuestion,
+ answer = AnswerUiState(
+ selectedChoiceIndex = 1,
+ ),
+ ),
+ onSelect = {},
)
}
}
diff --git a/ui/quiz/src/main/kotlin/dev/adriankuta/kahootquiz/ui/quiz/QuizScreenViewModel.kt b/ui/quiz/src/main/kotlin/dev/adriankuta/kahootquiz/ui/quiz/QuizScreenViewModel.kt
index 1249682..9e2ebad 100644
--- a/ui/quiz/src/main/kotlin/dev/adriankuta/kahootquiz/ui/quiz/QuizScreenViewModel.kt
+++ b/ui/quiz/src/main/kotlin/dev/adriankuta/kahootquiz/ui/quiz/QuizScreenViewModel.kt
@@ -6,26 +6,42 @@ import dagger.hilt.android.lifecycle.HiltViewModel
import dev.adriankuta.kahootquiz.domain.models.Question
import dev.adriankuta.kahootquiz.domain.usecases.GetQuizUseCase
import kotlinx.coroutines.flow.MutableStateFlow
+import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.asStateFlow
-import kotlinx.coroutines.launch
+import kotlinx.coroutines.flow.asFlow
+import kotlinx.coroutines.flow.combine
+import kotlinx.coroutines.flow.stateIn
import javax.inject.Inject
@HiltViewModel
class QuizScreenViewModel @Inject constructor(
- private val getQuizUseCase: GetQuizUseCase
+ private val getQuizUseCase: GetQuizUseCase,
) : ViewModel() {
- private val _uiState = MutableStateFlow(QuizUiState())
- val uiState: StateFlow = _uiState.asStateFlow()
+ private val _selectedChoiceIndex = MutableStateFlow(null)
+ val uiState: StateFlow = combine(
+ suspend { getQuizUseCase() }.asFlow(),
+ _selectedChoiceIndex,
+ ) { quiz, selectedChoiceIndex ->
+ QuizUiState(
+ currentQuestion = quiz.questions.first(),
+ answer = selectedChoiceIndex?.let { AnswerUiState(it) },
+ )
+ }.stateIn(
+ scope = viewModelScope,
+ started = SharingStarted.WhileSubscribed(5000),
+ initialValue = QuizUiState(),
+ )
- init {
- viewModelScope.launch {
- _uiState.value = QuizUiState(getQuizUseCase().questions.first())
- }
+ fun onChoiceSelected(index: Int) {
+ _selectedChoiceIndex.value = index
}
-
}
data class QuizUiState(
- val currentQuestion: Question? = null
-)
\ No newline at end of file
+ val currentQuestion: Question? = null,
+ val answer: AnswerUiState? = null,
+)
+
+data class AnswerUiState(
+ val selectedChoiceIndex: Int,
+)