@@ -17,33 +17,39 @@ import com.leanite.dynaquiz.core.domain.usecase.SaveQuizSessionUseCase
1717import com.leanite.dynaquiz.core.domain.usecase.SubmitAnswerUseCase
1818import kotlinx.collections.immutable.toImmutableList
1919import kotlinx.coroutines.Deferred
20- import kotlinx.coroutines.Job
2120import kotlinx.coroutines.async
2221import kotlinx.coroutines.channels.Channel
23- import kotlinx.coroutines.delay
2422import kotlinx.coroutines.flow.MutableStateFlow
2523import kotlinx.coroutines.flow.StateFlow
2624import kotlinx.coroutines.flow.asStateFlow
2725import kotlinx.coroutines.flow.receiveAsFlow
2826import kotlinx.coroutines.flow.update
2927import kotlinx.coroutines.launch
30- import kotlin.time.Duration.Companion.seconds
3128
3229class QuizViewModel (
3330 private val setup : QuizSetup ,
3431 private val getRandomQuestionUseCase : GetRandomQuestionUseCase ,
3532 private val submitAnswerUseCase : SubmitAnswerUseCase ,
3633 private val saveQuizSessionUseCase : SaveQuizSessionUseCase ,
34+ private val timer : QuizTimerController ,
3735) : ViewModel() {
3836 private val _uiState = MutableStateFlow (QuizUiState (challengeMode = setup.challengeMode))
3937 val uiState: StateFlow <QuizUiState > = _uiState .asStateFlow()
4038
4139 private val _events = Channel <QuizEvent >(Channel .BUFFERED )
4240 val events = _events .receiveAsFlow()
4341
44- private var timerJob: Job ? = null
4542 private var prefetchedNextQuestion: Deferred <Question ?>? = null
4643
44+ init {
45+ // Reflete o timer no UiState sem o VM gerenciar a coroutine
46+ viewModelScope.launch {
47+ timer.timeRemaining.collect { sec ->
48+ _uiState .update { it.copy(timeRemainingSec = sec) }
49+ }
50+ }
51+ }
52+
4753 fun onIntent (intent : QuizIntent ) {
4854 when (intent) {
4955 QuizIntent .Started -> start()
@@ -73,38 +79,23 @@ class QuizViewModel(
7379 }
7480
7581 private suspend fun runCountdown () {
76- for (sec in QuizRules .INITIAL_COUNTDOWN_SECONDS downTo 1 ) {
82+ timer.runCountdown( QuizRules .INITIAL_COUNTDOWN_SECONDS ) { sec ->
7783 _uiState .update { it.copy(phase = QuizPhase .Countdown (sec)) }
78- delay(1 .seconds)
7984 }
8085 _uiState .update { it.copy(phase = QuizPhase .Loading ) }
8186 }
8287
8388 private fun startPlaying (question : Question ) {
8489 _uiState .update { it.copy(phase = QuizPhase .Playing (question = question)) }
85- startTimer ()
90+ startQuestionTimer ()
8691 schedulePrefetchOfNextQuestion()
8792 }
8893
89- private fun startTimer () {
90- timerJob?.cancel()
94+ private fun startQuestionTimer () {
9195 val mode = _uiState .value.challengeMode
92-
93- if (mode !is ChallengeMode .Timed ) {
94- _uiState .update { it.copy(timeRemainingSec = null ) }
95- return
96+ if (mode is ChallengeMode .Timed ) {
97+ timer.start(viewModelScope, mode.perQuestionSeconds) { onTimeOut() }
9698 }
97-
98- val totalSeconds = mode.perQuestionSeconds
99- timerJob =
100- viewModelScope.launch {
101- for (sec in totalSeconds downTo 1 ) {
102- _uiState .update { it.copy(timeRemainingSec = sec) }
103- delay(1 .seconds)
104- }
105- _uiState .update { it.copy(timeRemainingSec = 0 ) }
106- onTimeOut()
107- }
10899 }
109100
110101 private fun onTimeOut () {
@@ -150,7 +141,7 @@ class QuizViewModel(
150141 playing : QuizPhase .Playing ,
151142 answer : String ,
152143 ) {
153- timerJob? .cancel()
144+ timer .cancel()
154145 _uiState .update {
155146 it.copy(phase = playing.copy(selectedAnswer = answer, isSubmitting = true ))
156147 }
@@ -194,12 +185,12 @@ class QuizViewModel(
194185 finalIndex : Int ,
195186 ) {
196187 val mode = _uiState .value.challengeMode
188+ timer.stop()
197189 _uiState .update {
198190 it.copy(
199191 phase = QuizPhase .Completed ,
200192 answerLog = answerLog.toImmutableList(),
201193 currentQuestionIndex = finalIndex,
202- timeRemainingSec = null ,
203194 )
204195 }
205196
@@ -248,7 +239,7 @@ class QuizViewModel(
248239 currentQuestionIndex = nextIndex,
249240 )
250241 }
251- startTimer ()
242+ startQuestionTimer ()
252243 schedulePrefetchOfNextQuestion()
253244 }
254245
@@ -266,7 +257,7 @@ class QuizViewModel(
266257 private fun currentPlaying (): QuizPhase .Playing ? = _uiState .value.phase as ? QuizPhase .Playing
267258
268259 override fun onCleared () {
269- timerJob? .cancel()
260+ timer .cancel()
270261 prefetchedNextQuestion?.cancel()
271262 super .onCleared()
272263 }
0 commit comments