fix: Correct timer behavior and display

This commit addresses issues with the question timer in `QuizScreen`.

Key changes:

- **QuizScreenViewModel:**
    - Initialize `_remainingTimeSeconds` to `0` instead of `-1` to prevent premature timer display.
    - Start timer only if `timerJob` is `null` (not already running) and it's the first question.
    - Set `timerJob` to `null` when it's cancelled (on choice selection, moving to next question, or finishing the quiz).
    - If a question has no time limit (null or <= 0 seconds), set `_remainingTimeSeconds` to `0` and do not start the `timerJob`.
    - When the timer finishes and no choice was selected, set `_selectedChoiceIndex` to `-1` (indicating time ran out) and ensure `timerJob` is nullified.
- **QuizScreen:**
    - Conditionally display the `TimerBar` only if `selectedChoiceIndex` is `null` (no choice made yet) AND `timerState.totalTimeSeconds` is greater than `0` (meaning there's a valid timer for the current question). This prevents showing a timer when a question has no time limit.
This commit is contained in:
2025-09-04 17:59:36 +02:00
parent d2fce7e7b9
commit 7d38facda5
2 changed files with 12 additions and 7 deletions

View File

@@ -129,7 +129,7 @@ private fun QuizScreen(
} }
// Timer below choices // Timer below choices
if (uiState.selectedChoiceIndex == null) { if (uiState.selectedChoiceIndex == null && uiState.timerState.totalTimeSeconds > 0) {
item { item {
TimerBar( TimerBar(
totalSeconds = uiState.timerState.totalTimeSeconds, totalSeconds = uiState.timerState.totalTimeSeconds,

View File

@@ -12,10 +12,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filterIsInstance
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.flow.flow import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@@ -34,7 +31,7 @@ class QuizScreenViewModel @Inject constructor(
initialValue = QuizUiState.Loading, initialValue = QuizUiState.Loading,
) )
private val _selectedChoiceIndex = MutableStateFlow<Int?>(null) private val _selectedChoiceIndex = MutableStateFlow<Int?>(null)
private val _remainingTimeSeconds = MutableStateFlow(-1) private val _remainingTimeSeconds = MutableStateFlow(0)
private val _currentQuestionIndex = MutableStateFlow(0) private val _currentQuestionIndex = MutableStateFlow(0)
private var timerJob: Job? = null private var timerJob: Job? = null
@@ -44,7 +41,7 @@ class QuizScreenViewModel @Inject constructor(
quiz.collect { quizState -> quiz.collect { quizState ->
if (quizState is QuizUiState.Success) { if (quizState is QuizUiState.Success) {
// Start only if timer hasn't been started yet and we are on the first question // Start only if timer hasn't been started yet and we are on the first question
if (_remainingTimeSeconds.value == -1 && _currentQuestionIndex.value == 0) { if (timerJob == null && _currentQuestionIndex.value == 0) {
val firstQuestionTime = quizState.quiz.questions.getOrNull(0)?.time?.inWholeSeconds?.toInt() val firstQuestionTime = quizState.quiz.questions.getOrNull(0)?.time?.inWholeSeconds?.toInt()
startCountdown(firstQuestionTime) startCountdown(firstQuestionTime)
} }
@@ -86,6 +83,7 @@ class QuizScreenViewModel @Inject constructor(
fun onChoiceSelected(index: Int) { fun onChoiceSelected(index: Int) {
timerJob?.cancel() timerJob?.cancel()
timerJob = null
_selectedChoiceIndex.value = index _selectedChoiceIndex.value = index
} }
@@ -103,13 +101,19 @@ class QuizScreenViewModel @Inject constructor(
} else { } else {
// Last question reached: stop timer and keep state (could navigate to results in the future) // Last question reached: stop timer and keep state (could navigate to results in the future)
timerJob?.cancel() timerJob?.cancel()
timerJob = null
_remainingTimeSeconds.value = 0
} }
} }
} }
private fun startCountdown(totalSeconds: Int?) { private fun startCountdown(totalSeconds: Int?) {
timerJob?.cancel() timerJob?.cancel()
if (totalSeconds == null || totalSeconds <= 0) return if (totalSeconds == null || totalSeconds <= 0) {
_remainingTimeSeconds.value = 0
timerJob = null
return
}
_remainingTimeSeconds.value = totalSeconds _remainingTimeSeconds.value = totalSeconds
timerJob = viewModelScope.launch { timerJob = viewModelScope.launch {
var remaining = totalSeconds var remaining = totalSeconds
@@ -122,6 +126,7 @@ class QuizScreenViewModel @Inject constructor(
if (_selectedChoiceIndex.value == null) { if (_selectedChoiceIndex.value == null) {
_selectedChoiceIndex.value = -1 _selectedChoiceIndex.value = -1
} }
timerJob = null
} }
} }
} }