mirror of
https://github.com/AdrianKuta/KahootQuiz.git
synced 2025-09-14 17:24:21 +02:00
feat: Refactor ViewModel state production and add lint baselines
This commit refactors how `ScreenUiState` is produced in `QuizScreenViewModel` by extracting the logic into a private `screenUiState` function that combines various flows. It also introduces `Result` sealed interface and `asResult()` Flow extension for handling asynchronous operations. Additionally, lint baseline files have been added to all modules and a Detekt configuration file for the `ui:quiz` module. Key changes: - **ViewModel Refactoring (`ui:quiz` module):** - Introduced `Result.kt` with a sealed interface for `Success`, `Error`, and `Loading` states, along with an `asResult()` extension function for `Flow` to wrap emissions in `Result`. - In `QuizScreenViewModel.kt`: - The `quiz` state flow now uses `asResult()` and maps the `Result` to `QuizUiState`, handling potential error states by defaulting to `QuizUiState.Loading` (actual error UI handling is noted as a TODO). - The logic for combining flows to produce `ScreenUiState` has been extracted into a new private function `screenUiState()`. This function takes the necessary flows as parameters and returns a `Flow<ScreenUiState>`. - The `uiState` flow now uses this new `screenUiState()` function for its production. - **Lint and Detekt Configuration:** - Added `lint-baseline.xml` files to the following modules to establish a baseline for lint warnings: - `app` - `core/designsystem` - `core/network` - `domain` - `model/data` - `ui/quiz` - Added `config/detekt/detekt.yml` to the `ui:quiz` module to configure Detekt rules, including exceptions for Jetpack Compose naming conventions, complexity rules for Composable functions, and style guidelines for magic numbers and unused private members. It also enables trailing commas for call sites and declaration sites. - **Binary File:** - Added `App.apk`.
This commit is contained in:
158
app/lint-baseline.xml
Normal file
158
app/lint-baseline.xml
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<issues format="6" by="lint 8.13.0-rc02" type="baseline" client="gradle" dependencies="false" name="AGP (8.13.0-rc02)" variant="all" version="8.13.0-rc02">
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedAttribute"
|
||||||
|
message="Attribute `endX` is only used in API level 24 and higher (current min is 23)"
|
||||||
|
errorLine1=" android:endX="85.84757""
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/ic_launcher_foreground.xml"
|
||||||
|
line="10"
|
||||||
|
column="17"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedAttribute"
|
||||||
|
message="Attribute `endY` is only used in API level 24 and higher (current min is 23)"
|
||||||
|
errorLine1=" android:endY="92.4963""
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/ic_launcher_foreground.xml"
|
||||||
|
line="11"
|
||||||
|
column="17"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedAttribute"
|
||||||
|
message="Attribute `startX` is only used in API level 24 and higher (current min is 23)"
|
||||||
|
errorLine1=" android:startX="42.9492""
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/ic_launcher_foreground.xml"
|
||||||
|
line="12"
|
||||||
|
column="17"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedAttribute"
|
||||||
|
message="Attribute `startY` is only used in API level 24 and higher (current min is 23)"
|
||||||
|
errorLine1=" android:startY="49.59793""
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/ic_launcher_foreground.xml"
|
||||||
|
line="13"
|
||||||
|
column="17"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedAttribute"
|
||||||
|
message="Attribute `offset` is only used in API level 24 and higher (current min is 23)"
|
||||||
|
errorLine1=" android:offset="0.0" />"
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/ic_launcher_foreground.xml"
|
||||||
|
line="17"
|
||||||
|
column="21"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedAttribute"
|
||||||
|
message="Attribute `offset` is only used in API level 24 and higher (current min is 23)"
|
||||||
|
errorLine1=" android:offset="1.0" />"
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/ic_launcher_foreground.xml"
|
||||||
|
line="20"
|
||||||
|
column="21"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedAttribute"
|
||||||
|
message="Attribute `fillType` is only used in API level 24 and higher (current min is 23)"
|
||||||
|
errorLine1=" android:fillType="nonZero""
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/ic_launcher_foreground.xml"
|
||||||
|
line="26"
|
||||||
|
column="9"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedResources"
|
||||||
|
message="The resource `R.color.purple_200` appears to be unused"
|
||||||
|
errorLine1=" <color name="purple_200">#FFBB86FC</color>"
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/values/colors.xml"
|
||||||
|
line="3"
|
||||||
|
column="12"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedResources"
|
||||||
|
message="The resource `R.color.purple_500` appears to be unused"
|
||||||
|
errorLine1=" <color name="purple_500">#FF6200EE</color>"
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/values/colors.xml"
|
||||||
|
line="4"
|
||||||
|
column="12"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedResources"
|
||||||
|
message="The resource `R.color.purple_700` appears to be unused"
|
||||||
|
errorLine1=" <color name="purple_700">#FF3700B3</color>"
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/values/colors.xml"
|
||||||
|
line="5"
|
||||||
|
column="12"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedResources"
|
||||||
|
message="The resource `R.color.teal_200` appears to be unused"
|
||||||
|
errorLine1=" <color name="teal_200">#FF03DAC5</color>"
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/values/colors.xml"
|
||||||
|
line="6"
|
||||||
|
column="12"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedResources"
|
||||||
|
message="The resource `R.color.teal_700` appears to be unused"
|
||||||
|
errorLine1=" <color name="teal_700">#FF018786</color>"
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/values/colors.xml"
|
||||||
|
line="7"
|
||||||
|
column="12"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedResources"
|
||||||
|
message="The resource `R.color.black` appears to be unused"
|
||||||
|
errorLine1=" <color name="black">#FF000000</color>"
|
||||||
|
errorLine2=" ~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/values/colors.xml"
|
||||||
|
line="8"
|
||||||
|
column="12"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedResources"
|
||||||
|
message="The resource `R.color.white` appears to be unused"
|
||||||
|
errorLine1=" <color name="white">#FFFFFFFF</color>"
|
||||||
|
errorLine2=" ~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/values/colors.xml"
|
||||||
|
line="9"
|
||||||
|
column="12"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
</issues>
|
176
core/designsystem/lint-baseline.xml
Normal file
176
core/designsystem/lint-baseline.xml
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<issues format="6" by="lint 8.13.0-rc02" type="baseline" client="gradle" dependencies="false" name="AGP (8.13.0-rc02)" variant="all" version="8.13.0-rc02">
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedAttribute"
|
||||||
|
message="Attribute `fillType` is only used in API level 24 and higher (current min is 23)"
|
||||||
|
errorLine1=" android:fillType="evenOdd""
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/ic_type.xml"
|
||||||
|
line="8"
|
||||||
|
column="9"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedAttribute"
|
||||||
|
message="Attribute `fillType` is only used in API level 24 and higher (current min is 23)"
|
||||||
|
errorLine1=" android:fillType="evenOdd""
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/ic_type.xml"
|
||||||
|
line="12"
|
||||||
|
column="9"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedAttribute"
|
||||||
|
message="Attribute `fillType` is only used in API level 24 and higher (current min is 23)"
|
||||||
|
errorLine1=" android:fillType="evenOdd""
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/ic_type.xml"
|
||||||
|
line="16"
|
||||||
|
column="9"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedAttribute"
|
||||||
|
message="Attribute `fillType` is only used in API level 24 and higher (current min is 23)"
|
||||||
|
errorLine1=" android:fillType="evenOdd""
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/ic_type.xml"
|
||||||
|
line="20"
|
||||||
|
column="9"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedAttribute"
|
||||||
|
message="Attribute `fillType` is only used in API level 24 and higher (current min is 23)"
|
||||||
|
errorLine1=" android:fillType="evenOdd""
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/ic_type.xml"
|
||||||
|
line="24"
|
||||||
|
column="9"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedAttribute"
|
||||||
|
message="Attribute `fillType` is only used in API level 24 and higher (current min is 23)"
|
||||||
|
errorLine1=" android:fillType="evenOdd""
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/ic_type.xml"
|
||||||
|
line="28"
|
||||||
|
column="9"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedAttribute"
|
||||||
|
message="Attribute `fillType` is only used in API level 24 and higher (current min is 23)"
|
||||||
|
errorLine1=" android:fillType="evenOdd""
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/ic_type.xml"
|
||||||
|
line="32"
|
||||||
|
column="9"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedAttribute"
|
||||||
|
message="Attribute `fillType` is only used in API level 24 and higher (current min is 23)"
|
||||||
|
errorLine1=" android:fillType="evenOdd""
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/ic_type.xml"
|
||||||
|
line="36"
|
||||||
|
column="9"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedAttribute"
|
||||||
|
message="Attribute `fillType` is only used in API level 24 and higher (current min is 23)"
|
||||||
|
errorLine1=" android:fillType="evenOdd""
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/ic_type.xml"
|
||||||
|
line="40"
|
||||||
|
column="9"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedAttribute"
|
||||||
|
message="Attribute `fillType` is only used in API level 24 and higher (current min is 23)"
|
||||||
|
errorLine1=" android:fillType="evenOdd""
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/ic_type.xml"
|
||||||
|
line="44"
|
||||||
|
column="9"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedAttribute"
|
||||||
|
message="Attribute `fillType` is only used in API level 24 and higher (current min is 23)"
|
||||||
|
errorLine1=" android:fillType="evenOdd""
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/ic_type.xml"
|
||||||
|
line="48"
|
||||||
|
column="9"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedAttribute"
|
||||||
|
message="Attribute `fillType` is only used in API level 24 and higher (current min is 23)"
|
||||||
|
errorLine1=" android:fillType="evenOdd""
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/ic_wrong.xml"
|
||||||
|
line="22"
|
||||||
|
column="9"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="UnusedAttribute"
|
||||||
|
message="Attribute `fillType` is only used in API level 24 and higher (current min is 23)"
|
||||||
|
errorLine1=" android:fillType="evenOdd""
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/ic_wrong.xml"
|
||||||
|
line="27"
|
||||||
|
column="13"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="VectorRaster"
|
||||||
|
message="This tag is not supported in images generated from this vector icon for API < 24; check generated icon to make sure it looks acceptable"
|
||||||
|
errorLine1=" <clip-path android:pathData="M5.853,5.721h28.284v28.284h-28.284z" />"
|
||||||
|
errorLine2=" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/ic_wrong.xml"
|
||||||
|
line="25"
|
||||||
|
column="9"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="VectorRaster"
|
||||||
|
message="This tag is not supported in images generated from this vector icon for API < 24; check generated icon to make sure it looks acceptable"
|
||||||
|
errorLine1=" <clip-path"
|
||||||
|
errorLine2=" ^">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/ic_wrong.xml"
|
||||||
|
line="26"
|
||||||
|
column="9"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
<issue
|
||||||
|
id="IconLocation"
|
||||||
|
message="Found bitmap drawable `res/drawable/bg_image.webp` in densityless folder">
|
||||||
|
<location
|
||||||
|
file="src/main/res/drawable/bg_image.webp"/>
|
||||||
|
</issue>
|
||||||
|
|
||||||
|
</issues>
|
4
core/network/lint-baseline.xml
Normal file
4
core/network/lint-baseline.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<issues format="6" by="lint 8.13.0-rc02" type="baseline" client="gradle" dependencies="false" name="AGP (8.13.0-rc02)" variant="all" version="8.13.0-rc02">
|
||||||
|
|
||||||
|
</issues>
|
4
domain/lint-baseline.xml
Normal file
4
domain/lint-baseline.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<issues format="6" by="lint 8.13.0-rc02" type="baseline" client="gradle" dependencies="false" name="AGP (8.13.0-rc02)" variant="all" version="8.13.0-rc02">
|
||||||
|
|
||||||
|
</issues>
|
4
model/data/lint-baseline.xml
Normal file
4
model/data/lint-baseline.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<issues format="6" by="lint 8.13.0-rc02" type="baseline" client="gradle" dependencies="false" name="AGP (8.13.0-rc02)" variant="all" version="8.13.0-rc02">
|
||||||
|
|
||||||
|
</issues>
|
33
ui/quiz/config/detekt/detekt.yml
Normal file
33
ui/quiz/config/detekt/detekt.yml
Normal file
@@ -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
|
4
ui/quiz/lint-baseline.xml
Normal file
4
ui/quiz/lint-baseline.xml
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<issues format="6" by="lint 8.13.0-rc02" type="baseline" client="gradle" dependencies="false" name="AGP (8.13.0-rc02)" variant="all" version="8.13.0-rc02">
|
||||||
|
|
||||||
|
</issues>
|
@@ -6,13 +6,17 @@ import dagger.hilt.android.lifecycle.HiltViewModel
|
|||||||
import dev.adriankuta.kahootquiz.domain.models.Question
|
import dev.adriankuta.kahootquiz.domain.models.Question
|
||||||
import dev.adriankuta.kahootquiz.domain.models.Quiz
|
import dev.adriankuta.kahootquiz.domain.models.Quiz
|
||||||
import dev.adriankuta.kahootquiz.domain.usecases.GetQuizUseCase
|
import dev.adriankuta.kahootquiz.domain.usecases.GetQuizUseCase
|
||||||
|
import dev.adriankuta.kahootquiz.ui.quiz.utils.Result
|
||||||
|
import dev.adriankuta.kahootquiz.ui.quiz.utils.asResult
|
||||||
import kotlinx.coroutines.Job
|
import kotlinx.coroutines.Job
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
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.flow
|
import kotlinx.coroutines.flow.flow
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
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
|
||||||
@@ -26,6 +30,14 @@ class QuizScreenViewModel @Inject constructor(
|
|||||||
private val quiz: StateFlow<QuizUiState> = flow {
|
private val quiz: StateFlow<QuizUiState> = flow {
|
||||||
emit(QuizUiState.Success(getQuizUseCase()))
|
emit(QuizUiState.Success(getQuizUseCase()))
|
||||||
}
|
}
|
||||||
|
.asResult()
|
||||||
|
.map { quizResult ->
|
||||||
|
when (quizResult) {
|
||||||
|
is Result.Error -> QuizUiState.Loading // Todo error handling not implemented on UI
|
||||||
|
Result.Loading -> QuizUiState.Loading
|
||||||
|
is Result.Success -> quizResult.data
|
||||||
|
}
|
||||||
|
}
|
||||||
.stateIn(
|
.stateIn(
|
||||||
scope = viewModelScope,
|
scope = viewModelScope,
|
||||||
started = SharingStarted.WhileSubscribed(5_000),
|
started = SharingStarted.WhileSubscribed(5_000),
|
||||||
@@ -52,34 +64,12 @@ class QuizScreenViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val uiState: StateFlow<ScreenUiState> = combine(
|
val uiState: StateFlow<ScreenUiState> = screenUiState(
|
||||||
quiz,
|
quizFlow = quiz,
|
||||||
_selectedChoiceIndex,
|
selectedChoiceIndexFlow = _selectedChoiceIndex,
|
||||||
_remainingTimeSeconds,
|
remainingTimeSecondsFlow = _remainingTimeSeconds,
|
||||||
_currentQuestionIndex,
|
currentQuestionIndexFlow = _currentQuestionIndex,
|
||||||
) { quizState, selectedChoiceIndex, remainingTimeSeconds, currentQuestionIndex ->
|
)
|
||||||
when (quizState) {
|
|
||||||
QuizUiState.Loading -> ScreenUiState.Loading
|
|
||||||
is QuizUiState.Success -> {
|
|
||||||
val currentQuestion = quizState.quiz.questions.getOrNull(currentQuestionIndex)
|
|
||||||
val isAnswerCorrect = selectedChoiceIndex?.let { idx ->
|
|
||||||
currentQuestion?.choices?.getOrNull(idx)?.correct == true
|
|
||||||
}
|
|
||||||
|
|
||||||
ScreenUiState.Success(
|
|
||||||
currentQuestion = currentQuestion,
|
|
||||||
selectedChoiceIndex = selectedChoiceIndex,
|
|
||||||
currentQuestionIndex = currentQuestionIndex,
|
|
||||||
totalQuestions = quizState.quiz.questions.size,
|
|
||||||
timerState = TimerState(
|
|
||||||
remainingTimeSeconds = remainingTimeSeconds,
|
|
||||||
totalTimeSeconds = currentQuestion?.time?.inWholeSeconds?.toInt() ?: 0,
|
|
||||||
),
|
|
||||||
isAnswerCorrect = isAnswerCorrect,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.stateIn(
|
.stateIn(
|
||||||
scope = viewModelScope,
|
scope = viewModelScope,
|
||||||
started = SharingStarted.WhileSubscribed(5_000),
|
started = SharingStarted.WhileSubscribed(5_000),
|
||||||
@@ -137,6 +127,40 @@ class QuizScreenViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun screenUiState(
|
||||||
|
quizFlow: StateFlow<QuizUiState>,
|
||||||
|
selectedChoiceIndexFlow: Flow<Int?>,
|
||||||
|
remainingTimeSecondsFlow: Flow<Int>,
|
||||||
|
currentQuestionIndexFlow: Flow<Int>,
|
||||||
|
): Flow<ScreenUiState> = combine(
|
||||||
|
quizFlow,
|
||||||
|
selectedChoiceIndexFlow,
|
||||||
|
remainingTimeSecondsFlow,
|
||||||
|
currentQuestionIndexFlow,
|
||||||
|
) { quizState, selectedChoiceIndex, remainingTimeSeconds, currentQuestionIndex ->
|
||||||
|
when (quizState) {
|
||||||
|
QuizUiState.Loading -> ScreenUiState.Loading
|
||||||
|
is QuizUiState.Success -> {
|
||||||
|
val currentQuestion = quizState.quiz.questions.getOrNull(currentQuestionIndex)
|
||||||
|
val isAnswerCorrect = selectedChoiceIndex?.let { idx ->
|
||||||
|
currentQuestion?.choices?.getOrNull(idx)?.correct == true
|
||||||
|
}
|
||||||
|
|
||||||
|
ScreenUiState.Success(
|
||||||
|
currentQuestion = currentQuestion,
|
||||||
|
selectedChoiceIndex = selectedChoiceIndex,
|
||||||
|
currentQuestionIndex = currentQuestionIndex,
|
||||||
|
totalQuestions = quizState.quiz.questions.size,
|
||||||
|
timerState = TimerState(
|
||||||
|
remainingTimeSeconds = remainingTimeSeconds,
|
||||||
|
totalTimeSeconds = currentQuestion?.time?.inWholeSeconds?.toInt() ?: 0,
|
||||||
|
),
|
||||||
|
isAnswerCorrect = isAnswerCorrect,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sealed interface QuizUiState {
|
sealed interface QuizUiState {
|
||||||
data object Loading : QuizUiState
|
data object Loading : QuizUiState
|
||||||
data class Success(
|
data class Success(
|
||||||
|
@@ -0,0 +1,16 @@
|
|||||||
|
package dev.adriankuta.kahootquiz.ui.quiz.utils
|
||||||
|
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
import kotlinx.coroutines.flow.catch
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
|
import kotlinx.coroutines.flow.onStart
|
||||||
|
|
||||||
|
sealed interface Result<out T> {
|
||||||
|
data class Success<T>(val data: T) : Result<T>
|
||||||
|
data class Error(val exception: Throwable) : Result<Nothing>
|
||||||
|
data object Loading : Result<Nothing>
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> Flow<T>.asResult(): Flow<Result<T>> = map<T, Result<T>> { Result.Success(it) }
|
||||||
|
.onStart { emit(Result.Loading) }
|
||||||
|
.catch { emit(Result.Error(it)) }
|
Reference in New Issue
Block a user