add comprehensive logging for exercise setup and state transitions across screens and ViewModels
This commit is contained in:
@@ -58,6 +58,7 @@ import eu.gaudian.translator.model.Language
|
|||||||
import eu.gaudian.translator.model.TagCategory
|
import eu.gaudian.translator.model.TagCategory
|
||||||
import eu.gaudian.translator.model.VocabularyCategory
|
import eu.gaudian.translator.model.VocabularyCategory
|
||||||
import eu.gaudian.translator.model.VocabularyStage
|
import eu.gaudian.translator.model.VocabularyStage
|
||||||
|
import eu.gaudian.translator.utils.Log
|
||||||
import eu.gaudian.translator.utils.findActivity
|
import eu.gaudian.translator.utils.findActivity
|
||||||
import eu.gaudian.translator.view.composable.AppButton
|
import eu.gaudian.translator.view.composable.AppButton
|
||||||
import eu.gaudian.translator.view.composable.AppIcons
|
import eu.gaudian.translator.view.composable.AppIcons
|
||||||
@@ -117,6 +118,7 @@ fun StartExerciseScreen(
|
|||||||
var amount by remember { mutableStateOf(0) }
|
var amount by remember { mutableStateOf(0) }
|
||||||
androidx.compose.runtime.LaunchedEffect(totalItemCount) {
|
androidx.compose.runtime.LaunchedEffect(totalItemCount) {
|
||||||
amount = totalItemCount
|
amount = totalItemCount
|
||||||
|
Log.d("StartExercise", "Items to show updated: total=$totalItemCount, amount=$amount")
|
||||||
}
|
}
|
||||||
|
|
||||||
val updateConfig: (eu.gaudian.translator.viewmodel.ExerciseConfig) -> Unit = { config ->
|
val updateConfig: (eu.gaudian.translator.viewmodel.ExerciseConfig) -> Unit = { config ->
|
||||||
@@ -213,12 +215,15 @@ fun StartExerciseScreen(
|
|||||||
enabled = totalItemCount > 0 && amount > 0,
|
enabled = totalItemCount > 0 && amount > 0,
|
||||||
amount = amount,
|
amount = amount,
|
||||||
onStart = {
|
onStart = {
|
||||||
|
Log.d("StartExercise", "Start pressed. shuffleCards=${exerciseConfig.shuffleCards}, selectedAmount=$amount, items=${itemsToShow.size}, origin=${selectedOriginLanguage?.nameResId}, target=${selectedTargetLanguage?.nameResId}, pairs=${selectedPairsIds.size}, categories=${selectedCategoryIds.size}, stages=${selectedStages.size}")
|
||||||
val finalItems = if (exerciseConfig.shuffleCards) {
|
val finalItems = if (exerciseConfig.shuffleCards) {
|
||||||
itemsToShow.shuffled().take(amount)
|
itemsToShow.shuffled().take(amount)
|
||||||
} else {
|
} else {
|
||||||
itemsToShow.take(amount)
|
itemsToShow.take(amount)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Log.d("StartExercise", "Final items prepared: count=${finalItems.size}")
|
||||||
|
|
||||||
exerciseViewModel.startExerciseWithConfig(
|
exerciseViewModel.startExerciseWithConfig(
|
||||||
finalItems,
|
finalItems,
|
||||||
exerciseConfig.copy(
|
exerciseConfig.copy(
|
||||||
@@ -229,6 +234,7 @@ fun StartExerciseScreen(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
Log.d("StartExercise", "Navigating to vocabulary_exercise/false")
|
||||||
navController.navigate("vocabulary_exercise/false")
|
navController.navigate("vocabulary_exercise/false")
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
|
import eu.gaudian.translator.utils.Log
|
||||||
import eu.gaudian.translator.utils.findActivity
|
import eu.gaudian.translator.utils.findActivity
|
||||||
import eu.gaudian.translator.view.composable.AppAlertDialog
|
import eu.gaudian.translator.view.composable.AppAlertDialog
|
||||||
import eu.gaudian.translator.viewmodel.ScreenState
|
import eu.gaudian.translator.viewmodel.ScreenState
|
||||||
@@ -67,8 +68,10 @@ fun VocabularyExerciseHostScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(categoryIdsAsJson, stageNamesAsJson, languageIdsAsJson, dailyOnly) {
|
LaunchedEffect(categoryIdsAsJson, stageNamesAsJson, languageIdsAsJson, dailyOnly) {
|
||||||
|
Log.d("ExerciseHost", "LaunchedEffect filters: categories=$categoryIdsAsJson, stages=$stageNamesAsJson, languages=$languageIdsAsJson, dailyOnly=$dailyOnly")
|
||||||
// Only reset and prepare if the host is opened via explicit filters.
|
// Only reset and prepare if the host is opened via explicit filters.
|
||||||
if (!categoryIdsAsJson.isNullOrBlank() || !stageNamesAsJson.isNullOrBlank() || !languageIdsAsJson.isNullOrBlank() || dailyOnly) {
|
if (!categoryIdsAsJson.isNullOrBlank() || !stageNamesAsJson.isNullOrBlank() || !languageIdsAsJson.isNullOrBlank() || dailyOnly) {
|
||||||
|
Log.d("ExerciseHost", "Preparing exercise from filters")
|
||||||
exerciseViewModel.resetExercise()
|
exerciseViewModel.resetExercise()
|
||||||
vocabularyViewModel.prepareExercise(
|
vocabularyViewModel.prepareExercise(
|
||||||
categoryIdsAsJson,
|
categoryIdsAsJson,
|
||||||
@@ -76,10 +79,13 @@ fun VocabularyExerciseHostScreen(
|
|||||||
languageIdsAsJson,
|
languageIdsAsJson,
|
||||||
dailyOnly = dailyOnly,
|
dailyOnly = dailyOnly,
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
Log.d("ExerciseHost", "No filters provided; skipping prepareExercise")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(cardSet, screenState, pendingConfig) {
|
LaunchedEffect(cardSet, screenState, pendingConfig) {
|
||||||
|
Log.d("ExerciseHost", "State update: screenState=$screenState, cardSet=${cardSet?.cards?.size ?: 0}, pendingCount=${pendingConfig.exerciseItemCount}")
|
||||||
if (cardSet != null && screenState == ScreenState.START) {
|
if (cardSet != null && screenState == ScreenState.START) {
|
||||||
val items = cardSet?.cards.orEmpty()
|
val items = cardSet?.cards.orEmpty()
|
||||||
if (items.isNotEmpty()) {
|
if (items.isNotEmpty()) {
|
||||||
@@ -91,6 +97,7 @@ fun VocabularyExerciseHostScreen(
|
|||||||
} else {
|
} else {
|
||||||
items.take(selectedCount)
|
items.take(selectedCount)
|
||||||
}
|
}
|
||||||
|
Log.d("ExerciseHost", "Auto-starting exercise with ${finalItems.size} items")
|
||||||
exerciseViewModel.startExerciseWithConfig(
|
exerciseViewModel.startExerciseWithConfig(
|
||||||
finalItems,
|
finalItems,
|
||||||
pendingConfig.copy(
|
pendingConfig.copy(
|
||||||
@@ -98,52 +105,51 @@ fun VocabularyExerciseHostScreen(
|
|||||||
originalExerciseItems = finalItems
|
originalExerciseItems = finalItems
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
} else {
|
||||||
|
Log.d("ExerciseHost", "CardSet present but empty")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cardSet == null && screenState != ScreenState.START) {
|
when (screenState) {
|
||||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
ScreenState.START -> {
|
||||||
CircularProgressIndicator()
|
Log.d("ExerciseHost", "Rendering START screen")
|
||||||
|
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||||
|
CircularProgressIndicator()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
ScreenState.EXERCISE -> {
|
||||||
when (screenState) {
|
Log.d("ExerciseHost", "Rendering EXERCISE screen")
|
||||||
ScreenState.START -> {
|
ExerciseScreen(
|
||||||
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
viewModel = exerciseViewModel,
|
||||||
CircularProgressIndicator()
|
onClose = onClose,
|
||||||
}
|
onFinish = { score, wrong ->
|
||||||
}
|
@Suppress("AssignedValueIsNeverRead")
|
||||||
ScreenState.EXERCISE -> {
|
finalScore = score
|
||||||
ExerciseScreen(
|
@Suppress("AssignedValueIsNeverRead")
|
||||||
viewModel = exerciseViewModel,
|
finalWrongAnswers = wrong
|
||||||
onClose = onClose,
|
exerciseViewModel.finishExercise(score, wrong)
|
||||||
onFinish = { score, wrong ->
|
},
|
||||||
@Suppress("AssignedValueIsNeverRead")
|
navController = navController
|
||||||
finalScore = score
|
)
|
||||||
@Suppress("AssignedValueIsNeverRead")
|
}
|
||||||
finalWrongAnswers = wrong
|
ScreenState.RESULT -> {
|
||||||
exerciseViewModel.finishExercise(score, wrong)
|
Log.d("ExerciseHost", "Rendering RESULT screen")
|
||||||
},
|
val totalItems by exerciseViewModel.totalItems.collectAsState()
|
||||||
navController = navController
|
val originalItems by exerciseViewModel.originalItems.collectAsState()
|
||||||
)
|
ResultScreen(
|
||||||
}
|
score = finalScore,
|
||||||
ScreenState.RESULT -> {
|
wrongAnswers = finalWrongAnswers,
|
||||||
val totalItems by exerciseViewModel.totalItems.collectAsState()
|
totalItems = totalItems,
|
||||||
val originalItems by exerciseViewModel.originalItems.collectAsState()
|
onRestart = {
|
||||||
ResultScreen(
|
vocabularyViewModel.clearCardSet()
|
||||||
score = finalScore,
|
exerciseViewModel.resetExercise()
|
||||||
wrongAnswers = finalWrongAnswers,
|
},
|
||||||
totalItems = totalItems,
|
onRetryWrong = { _ ->
|
||||||
onRestart = {
|
exerciseViewModel.retryWrongAnswers(originalItems)
|
||||||
vocabularyViewModel.clearCardSet()
|
},
|
||||||
exerciseViewModel.resetExercise()
|
onClose = onClose
|
||||||
},
|
)
|
||||||
onRetryWrong = { _ ->
|
|
||||||
exerciseViewModel.retryWrongAnswers(originalItems)
|
|
||||||
},
|
|
||||||
onClose = onClose
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -109,6 +109,7 @@ class VocabularyExerciseViewModel @Inject constructor(
|
|||||||
types: Set<VocabularyExerciseType>,
|
types: Set<VocabularyExerciseType>,
|
||||||
shuffleLanguages: Boolean
|
shuffleLanguages: Boolean
|
||||||
) {
|
) {
|
||||||
|
Log.d("ExerciseVM", "startExercise called: items=${items.size}, types=$types, shuffleLanguages=$shuffleLanguages")
|
||||||
// Reset counters for the new exercise session
|
// Reset counters for the new exercise session
|
||||||
_correctAnswers.value = 0
|
_correctAnswers.value = 0
|
||||||
_wrongAnswers.value = 0
|
_wrongAnswers.value = 0
|
||||||
@@ -161,6 +162,7 @@ class VocabularyExerciseViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun loadExercise() {
|
private fun loadExercise() {
|
||||||
|
Log.d("ExerciseVM", "loadExercise: index=$currentIndex, total=${currentItems.size}")
|
||||||
if (currentIndex < currentItems.size) {
|
if (currentIndex < currentItems.size) {
|
||||||
// Ensure item categories align with exercise type by attempting a swap instead of replacement
|
// Ensure item categories align with exercise type by attempting a swap instead of replacement
|
||||||
val randomType = exerciseTypes.random()
|
val randomType = exerciseTypes.random()
|
||||||
@@ -276,8 +278,10 @@ class VocabularyExerciseViewModel @Inject constructor(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Log.d("ExerciseVM", "exerciseState set: type=$randomType, itemId=${itemToUse.id}")
|
||||||
} else {
|
} else {
|
||||||
_exerciseState.value = null // End of exercise
|
_exerciseState.value = null // End of exercise
|
||||||
|
Log.d("ExerciseVM", "loadExercise: end of exercise, state cleared")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -394,6 +398,7 @@ class VocabularyExerciseViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun onTrainingModeChanged(value: Boolean) {
|
fun onTrainingModeChanged(value: Boolean) {
|
||||||
|
Log.d("ExerciseVM", "onTrainingModeChanged: $value")
|
||||||
_trainingMode.value = value
|
_trainingMode.value = value
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,24 +406,29 @@ class VocabularyExerciseViewModel @Inject constructor(
|
|||||||
items: List<VocabularyItem>,
|
items: List<VocabularyItem>,
|
||||||
config: ExerciseConfig
|
config: ExerciseConfig
|
||||||
) {
|
) {
|
||||||
|
Log.d("ExerciseVM", "startExerciseWithConfig called: items=${items.size}, configCount=${config.exerciseItemCount}, shuffleCards=${config.shuffleCards}, shuffleLanguages=${config.shuffleLanguages}, trainingMode=${config.trainingMode}, dueTodayOnly=${config.dueTodayOnly}, types=${config.selectedExerciseTypes}")
|
||||||
_exerciseConfig.value = config
|
_exerciseConfig.value = config
|
||||||
_pendingExerciseConfig.value = config
|
_pendingExerciseConfig.value = config
|
||||||
_totalItems.value = items.size
|
_totalItems.value = items.size
|
||||||
_originalItems.value = items
|
_originalItems.value = items
|
||||||
startExercise(items, config.selectedExerciseTypes, config.shuffleLanguages)
|
startExercise(items, config.selectedExerciseTypes, config.shuffleLanguages)
|
||||||
_screenState.value = ScreenState.EXERCISE
|
_screenState.value = ScreenState.EXERCISE
|
||||||
|
Log.d("ExerciseVM", "screenState set to EXERCISE; totalItems=${_totalItems.value}")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun updatePendingExerciseConfig(config: ExerciseConfig) {
|
fun updatePendingExerciseConfig(config: ExerciseConfig) {
|
||||||
|
Log.d("ExerciseVM", "updatePendingExerciseConfig: count=${config.exerciseItemCount}, shuffleCards=${config.shuffleCards}, shuffleLanguages=${config.shuffleLanguages}, trainingMode=${config.trainingMode}, dueTodayOnly=${config.dueTodayOnly}, types=${config.selectedExerciseTypes}")
|
||||||
_pendingExerciseConfig.value = config
|
_pendingExerciseConfig.value = config
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun finishExercise(score: Int, wrongAnswers: Int) {
|
fun finishExercise(score: Int, wrongAnswers: Int) {
|
||||||
_exerciseResults.value = ExerciseResults(score, wrongAnswers)
|
_exerciseResults.value = ExerciseResults(score, wrongAnswers)
|
||||||
_screenState.value = ScreenState.RESULT
|
_screenState.value = ScreenState.RESULT
|
||||||
}
|
}
|
||||||
|
|
||||||
fun resetExercise() {
|
fun resetExercise() {
|
||||||
|
Log.d("ExerciseVM", "resetExercise called")
|
||||||
_screenState.value = ScreenState.START
|
_screenState.value = ScreenState.START
|
||||||
_exerciseConfig.value = ExerciseConfig()
|
_exerciseConfig.value = ExerciseConfig()
|
||||||
_exerciseResults.value = ExerciseResults()
|
_exerciseResults.value = ExerciseResults()
|
||||||
@@ -428,6 +438,7 @@ class VocabularyExerciseViewModel @Inject constructor(
|
|||||||
_exerciseState.value = null
|
_exerciseState.value = null
|
||||||
_totalItems.value = 0
|
_totalItems.value = 0
|
||||||
_originalItems.value = emptyList()
|
_originalItems.value = emptyList()
|
||||||
|
Log.d("ExerciseVM", "resetExercise completed; screenState=START")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun retryWrongAnswers(originalItems: List<VocabularyItem>) {
|
fun retryWrongAnswers(originalItems: List<VocabularyItem>) {
|
||||||
|
|||||||
@@ -714,6 +714,7 @@ class VocabularyViewModel @Inject constructor(
|
|||||||
languageIdsAsJson: String?,
|
languageIdsAsJson: String?,
|
||||||
dailyOnly: Boolean = false
|
dailyOnly: Boolean = false
|
||||||
) {
|
) {
|
||||||
|
Log.d("VocabularyVM", "prepareExercise: categories=$categoryIdsAsJson, stages=$stageNamesAsJson, languages=$languageIdsAsJson, dailyOnly=$dailyOnly")
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
val categoryList = categoryIdsAsJson?.takeIf { it.isNotBlank() }
|
val categoryList = categoryIdsAsJson?.takeIf { it.isNotBlank() }
|
||||||
?.split(",")
|
?.split(",")
|
||||||
@@ -732,6 +733,7 @@ class VocabularyViewModel @Inject constructor(
|
|||||||
allLangs.filter { it.nameResId in ids }
|
allLangs.filter { it.nameResId in ids }
|
||||||
} ?: emptyList()
|
} ?: emptyList()
|
||||||
|
|
||||||
|
Log.d("VocabularyVM", "prepareExercise parsed: categories=${categoryList.size}, stages=${stageList.size}, languages=${languageList.size}")
|
||||||
loadCardSet(categoryList, stageList, languageList, dailyOnly)
|
loadCardSet(categoryList, stageList, languageList, dailyOnly)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -743,6 +745,7 @@ class VocabularyViewModel @Inject constructor(
|
|||||||
languages: List<Language>? = null,
|
languages: List<Language>? = null,
|
||||||
dailyOnly: Boolean = false
|
dailyOnly: Boolean = false
|
||||||
) {
|
) {
|
||||||
|
Log.d(TAG, "loadCardSet invoked: categories=${categories?.size ?: 0}, stages=${stages?.size ?: 0}, languages=${languages?.map { it.nameResId }}, dailyOnly=$dailyOnly")
|
||||||
Log.d(TAG, "Loading card set with languages: $languages, categories: ${categories?.size}, stages: ${stages?.size}")
|
Log.d(TAG, "Loading card set with languages: $languages, categories: ${categories?.size}, stages: ${stages?.size}")
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
statusService.showLoadingMessage("Loading card set")
|
statusService.showLoadingMessage("Loading card set")
|
||||||
@@ -802,6 +805,8 @@ class VocabularyViewModel @Inject constructor(
|
|||||||
dueTodayOnly = dailyOnly
|
dueTodayOnly = dailyOnly
|
||||||
).first()
|
).first()
|
||||||
|
|
||||||
|
Log.d(TAG, "loadCardSet: filterVocabularyItems returned ${filteredItems.size} items")
|
||||||
|
|
||||||
Log.d(TAG, "loadCardSet: Filtering completed, found ${filteredItems.size} items")
|
Log.d(TAG, "loadCardSet: Filtering completed, found ${filteredItems.size} items")
|
||||||
|
|
||||||
if (filteredItems.isNotEmpty()) {
|
if (filteredItems.isNotEmpty()) {
|
||||||
|
|||||||
Reference in New Issue
Block a user