feat: Implement Answer Feedback Banner

This commit introduces an `AnswerFeedbackBanner` composable that displays whether a selected answer is correct or wrong. It's integrated into the `Toolbar` section of the `QuizScreen`.

Key changes:

- **UI Layer (`ui:quiz` module):**
    - Created `AnswerFeedbackBanner.kt` composable:
        - Displays "Correct" or "Wrong" text with a green or red background respectively.
        - Uses `Surface` for elevation and `zIndex` to appear above other elements.
    - In `QuizScreen.kt`:
        - Modified the `toolbar` item to include a `Box` that layers the `Toolbar` and the new `AnswerFeedbackBanner`.
        - The `AnswerFeedbackBanner` is shown when `uiState.isAnswerCorrect` is not null.
    - In `QuizScreenViewModel.kt`:
        - Added `isAnswerCorrect: Boolean?` to `ScreenUiState.Success`.
        - Calculated `isAnswerCorrect` based on the selected choice and the correct answer for the current question.
- **Resources (`ui:quiz` module):**
    - Added new string resources for "Correct" and "Wrong" in `strings.xml`.
This commit is contained in:
2025-09-04 22:04:40 +02:00
parent 12638f33d8
commit 99f1c49713
4 changed files with 66 additions and 8 deletions

View File

@@ -32,6 +32,7 @@ import dev.adriankuta.kahootquiz.core.designsystem.KahootQuizTheme
import dev.adriankuta.kahootquiz.domain.models.Choice import dev.adriankuta.kahootquiz.domain.models.Choice
import dev.adriankuta.kahootquiz.domain.models.Question import dev.adriankuta.kahootquiz.domain.models.Question
import dev.adriankuta.kahootquiz.ui.quiz.components.Choices import dev.adriankuta.kahootquiz.ui.quiz.components.Choices
import dev.adriankuta.kahootquiz.ui.quiz.components.AnswerFeedbackBanner
import dev.adriankuta.kahootquiz.ui.quiz.components.QuestionContent import dev.adriankuta.kahootquiz.ui.quiz.components.QuestionContent
import dev.adriankuta.kahootquiz.ui.quiz.components.TimerBar import dev.adriankuta.kahootquiz.ui.quiz.components.TimerBar
import dev.adriankuta.kahootquiz.ui.quiz.components.Toolbar import dev.adriankuta.kahootquiz.ui.quiz.components.Toolbar
@@ -115,16 +116,26 @@ private fun QuizScreenSuccess(
private fun LazyListScope.toolbar( private fun LazyListScope.toolbar(
uiState: ScreenUiState.Success, uiState: ScreenUiState.Success,
modifier: Modifier = Modifier,
) { ) {
item(key = "toolbar") { item(key = "toolbar") {
Toolbar( Box(
modifier = Modifier modifier = modifier
.fillMaxWidth() .height(72.dp),
.height(72.dp) ) {
.padding(8.dp), Toolbar(
currentQuestionIndex = uiState.currentQuestionIndex, modifier = Modifier
totalQuestions = uiState.totalQuestions, .fillMaxSize()
) .padding(8.dp),
currentQuestionIndex = uiState.currentQuestionIndex,
totalQuestions = uiState.totalQuestions,
)
uiState.isAnswerCorrect?.let { isCorrect ->
AnswerFeedbackBanner(
isCorrect = isCorrect,
)
}
}
} }
} }

View File

@@ -61,6 +61,9 @@ class QuizScreenViewModel @Inject constructor(
QuizUiState.Loading -> ScreenUiState.Loading QuizUiState.Loading -> ScreenUiState.Loading
is QuizUiState.Success -> { is QuizUiState.Success -> {
val currentQuestion = quizState.quiz.questions.getOrNull(currentQuestionIndex) val currentQuestion = quizState.quiz.questions.getOrNull(currentQuestionIndex)
val isAnswerCorrect = selectedChoiceIndex?.let { idx ->
currentQuestion?.choices?.getOrNull(idx)?.correct == true
}
ScreenUiState.Success( ScreenUiState.Success(
currentQuestion = currentQuestion, currentQuestion = currentQuestion,
@@ -71,6 +74,7 @@ class QuizScreenViewModel @Inject constructor(
remainingTimeSeconds = remainingTimeSeconds, remainingTimeSeconds = remainingTimeSeconds,
totalTimeSeconds = currentQuestion?.time?.inWholeSeconds?.toInt() ?: 0, totalTimeSeconds = currentQuestion?.time?.inWholeSeconds?.toInt() ?: 0,
), ),
isAnswerCorrect = isAnswerCorrect,
) )
} }
} }
@@ -148,6 +152,7 @@ sealed interface ScreenUiState {
val currentQuestionIndex: Int = 0, val currentQuestionIndex: Int = 0,
val totalQuestions: Int = 0, val totalQuestions: Int = 0,
val timerState: TimerState = TimerState(), val timerState: TimerState = TimerState(),
val isAnswerCorrect: Boolean? = null,
) : ScreenUiState ) : ScreenUiState
} }

View File

@@ -0,0 +1,40 @@
package dev.adriankuta.kahootquiz.ui.quiz.components
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import dev.adriankuta.kahootquiz.core.designsystem.Green
import dev.adriankuta.kahootquiz.core.designsystem.Red
import dev.adriankuta.kahootquiz.ui.quiz.R
@Composable
fun AnswerFeedbackBanner(
isCorrect: Boolean,
modifier: Modifier = Modifier,
) {
Surface(
modifier = modifier
.fillMaxSize()
.zIndex(10f),
shadowElevation = 8.dp,
color = if (isCorrect) Green else Red,
contentColor = Color.White,
) {
Box {
Text(
text = stringResource(if (isCorrect) R.string.correct else R.string.wrong),
textAlign = TextAlign.Center,
modifier = Modifier.align(Alignment.Center),
)
}
}
}

View File

@@ -2,4 +2,6 @@
<resources> <resources>
<string name="quiz">Quiz</string> <string name="quiz">Quiz</string>
<string name="continue_text">Continue</string> <string name="continue_text">Continue</string>
<string name="correct">Correct</string>
<string name="wrong">Wrong</string>
</resources> </resources>