Migrate ViewModels to Hilt dependency injection and refactor ViewModel instantiation across the app
This commit is contained in:
6
.idea/misc.xml
generated
6
.idea/misc.xml
generated
@@ -1,4 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
|
<component name="EntryPointsManager">
|
||||||
|
<list size="1">
|
||||||
|
<item index="0" class="java.lang.String" itemvalue="kotlinx.serialization.Serializable" />
|
||||||
|
</list>
|
||||||
|
</component>
|
||||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
|
||||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||||
|
|||||||
@@ -1,3 +1,5 @@
|
|||||||
|
@file:Suppress("unused", "HardCodedStringLiteral")
|
||||||
|
|
||||||
package eu.gaudian.translator.di
|
package eu.gaudian.translator.di
|
||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
@@ -11,10 +13,18 @@ import eu.gaudian.translator.model.repository.ApiRepository
|
|||||||
import eu.gaudian.translator.model.repository.DictionaryFileRepository
|
import eu.gaudian.translator.model.repository.DictionaryFileRepository
|
||||||
import eu.gaudian.translator.model.repository.DictionaryLookupRepository
|
import eu.gaudian.translator.model.repository.DictionaryLookupRepository
|
||||||
import eu.gaudian.translator.model.repository.DictionaryRepository
|
import eu.gaudian.translator.model.repository.DictionaryRepository
|
||||||
|
import eu.gaudian.translator.model.repository.ExerciseRepository
|
||||||
|
import eu.gaudian.translator.model.repository.LanguageConfigRepository
|
||||||
import eu.gaudian.translator.model.repository.LanguageRepository
|
import eu.gaudian.translator.model.repository.LanguageRepository
|
||||||
import eu.gaudian.translator.model.repository.SettingsRepository
|
import eu.gaudian.translator.model.repository.SettingsRepository
|
||||||
import eu.gaudian.translator.model.repository.VocabularyRepository
|
import eu.gaudian.translator.model.repository.VocabularyRepository
|
||||||
|
import eu.gaudian.translator.utils.CorrectionService
|
||||||
|
import eu.gaudian.translator.utils.ExerciseService
|
||||||
import eu.gaudian.translator.utils.StatusMessageService
|
import eu.gaudian.translator.utils.StatusMessageService
|
||||||
|
import eu.gaudian.translator.utils.TranslationService
|
||||||
|
import eu.gaudian.translator.utils.VocabularyService
|
||||||
|
import eu.gaudian.translator.utils.YouTubeApiService
|
||||||
|
import eu.gaudian.translator.utils.YouTubeExerciseService
|
||||||
import eu.gaudian.translator.utils.dictionary.DictionaryService
|
import eu.gaudian.translator.utils.dictionary.DictionaryService
|
||||||
import javax.inject.Singleton
|
import javax.inject.Singleton
|
||||||
|
|
||||||
@@ -58,6 +68,12 @@ object RepositoryModule {
|
|||||||
return LanguageRepository(application)
|
return LanguageRepository(application)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideLanguageConfigRepository(application: Application): LanguageConfigRepository {
|
||||||
|
return LanguageConfigRepository(application)
|
||||||
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun provideDictionaryFileRepository(application: Application): DictionaryFileRepository {
|
fun provideDictionaryFileRepository(application: Application): DictionaryFileRepository {
|
||||||
@@ -87,4 +103,46 @@ object RepositoryModule {
|
|||||||
fun provideStatusMessageService(): StatusMessageService {
|
fun provideStatusMessageService(): StatusMessageService {
|
||||||
return StatusMessageService
|
return StatusMessageService
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideVocabularyService(application: Application): VocabularyService {
|
||||||
|
return VocabularyService(application)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideExerciseRepository(application: Application): ExerciseRepository {
|
||||||
|
return ExerciseRepository(application)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideExerciseService(application: Application): ExerciseService {
|
||||||
|
return ExerciseService(application)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideTranslationService(application: Application): TranslationService {
|
||||||
|
return TranslationService(application)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideCorrectionService(application: Application): CorrectionService {
|
||||||
|
return CorrectionService(application)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideYouTubeApiService(): YouTubeApiService {
|
||||||
|
return YouTubeApiService
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun provideYouTubeExerciseService(): YouTubeExerciseService {
|
||||||
|
return YouTubeExerciseService
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,85 @@
|
|||||||
|
@file:Suppress("HardCodedStringLiteral")
|
||||||
|
|
||||||
|
package eu.gaudian.translator.model.repository
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import eu.gaudian.translator.model.grammar.LanguageConfig
|
||||||
|
import eu.gaudian.translator.utils.Log
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.serialization.json.Json
|
||||||
|
import javax.inject.Inject
|
||||||
|
import javax.inject.Singleton
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Repository for managing language configurations loaded from assets.
|
||||||
|
* This is a singleton that can be injected into multiple ViewModels.
|
||||||
|
*/
|
||||||
|
@Singleton
|
||||||
|
class LanguageConfigRepository @Inject constructor(
|
||||||
|
@param:ApplicationContext private val context: Context
|
||||||
|
) {
|
||||||
|
private val _configs = MutableStateFlow<Map<String, LanguageConfig>>(emptyMap())
|
||||||
|
val configs: StateFlow<Map<String, LanguageConfig>> = _configs.asStateFlow()
|
||||||
|
|
||||||
|
private val jsonParser = Json { ignoreUnknownKeys = true }
|
||||||
|
|
||||||
|
init {
|
||||||
|
loadAllConfigs()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Loads all language configuration files from the "language_configs" asset directory.
|
||||||
|
*/
|
||||||
|
private fun loadAllConfigs() {
|
||||||
|
try {
|
||||||
|
val configFiles = context.assets.list("language_configs") ?: return
|
||||||
|
val loadedConfigs = mutableMapOf<String, LanguageConfig>()
|
||||||
|
|
||||||
|
for (fileName in configFiles) {
|
||||||
|
if (fileName.endsWith(".json")) {
|
||||||
|
val jsonString = context.assets.open("language_configs/$fileName")
|
||||||
|
.bufferedReader()
|
||||||
|
.use { it.readText() }
|
||||||
|
|
||||||
|
val config = jsonParser.decodeFromString<LanguageConfig>(jsonString)
|
||||||
|
loadedConfigs[config.language_code] = config
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_configs.value = loadedConfigs
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("Failed to load language configs: ${e.message}")
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the configuration for a specific language code.
|
||||||
|
*
|
||||||
|
* @param langCode The ISO language code (e.g., "de").
|
||||||
|
* @return The LanguageConfig for the given code, or null if not found.
|
||||||
|
*/
|
||||||
|
fun getConfigForLanguage(langCode: String): LanguageConfig? {
|
||||||
|
Log.d("Fetching config for language: $langCode")
|
||||||
|
return _configs.value[langCode]
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the set of articles for a specific language code.
|
||||||
|
*
|
||||||
|
* @param langCode The ISO language code (e.g., "de").
|
||||||
|
* @return A Set<String> of articles for the given language. Returns an empty set if
|
||||||
|
* the language code is not found or if the language has no articles defined.
|
||||||
|
*/
|
||||||
|
fun getArticlesForLanguage(langCode: String): Set<String> {
|
||||||
|
return try {
|
||||||
|
val config = _configs.value[langCode]
|
||||||
|
config?.articles?.toSet() ?: emptySet()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
Log.e("Error retrieving articles for '$langCode': ${e.message}")
|
||||||
|
emptySet()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -45,12 +45,77 @@ object StatusMessageService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("unused")
|
||||||
fun showSimpleMessage(text: String, type: MessageDisplayType = MessageDisplayType.INFO) {
|
fun showSimpleMessage(text: String, type: MessageDisplayType = MessageDisplayType.INFO) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
_actions.emit(StatusAction.ShowMessage(text, type, 5))
|
_actions.emit(StatusAction.ShowMessage(text, type, 5))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun showErrorMessage(text: String, timeoutInSeconds: Int = 5) {
|
||||||
|
scope.launch {
|
||||||
|
_actions.emit(StatusAction.ShowMessage(
|
||||||
|
text, MessageDisplayType.ERROR,
|
||||||
|
timeoutInSeconds = timeoutInSeconds
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showLoadingMessage(text: String, timeoutInSeconds: Int = 0) {
|
||||||
|
scope.launch {
|
||||||
|
_actions.emit(StatusAction.ShowMessage(
|
||||||
|
text, MessageDisplayType.LOADING,
|
||||||
|
timeoutInSeconds = timeoutInSeconds
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showInfoMessage(text: String, timeoutInSeconds: Int = 3) {
|
||||||
|
scope.launch {
|
||||||
|
_actions.emit(StatusAction.ShowMessage(
|
||||||
|
text, MessageDisplayType.INFO,
|
||||||
|
timeoutInSeconds = timeoutInSeconds
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showSuccessMessage(text: String, timeoutInSeconds: Int = 3) {
|
||||||
|
scope.launch {
|
||||||
|
_actions.emit(StatusAction.ShowMessage(
|
||||||
|
text, MessageDisplayType.SUCCESS,
|
||||||
|
timeoutInSeconds = timeoutInSeconds
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showPermanentMessage(text: String, type: MessageDisplayType) {
|
||||||
|
scope.launch {
|
||||||
|
_actions.emit(StatusAction.ShowPermanentMessage(text, type))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancelPermanentMessage() {
|
||||||
|
scope.launch {
|
||||||
|
_actions.emit(StatusAction.CancelPermanentMessage)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun hideMessageBar() {
|
||||||
|
scope.launch {
|
||||||
|
_actions.emit(StatusAction.HideMessageBar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancelAllMessages() {
|
||||||
|
scope.launch {
|
||||||
|
_actions.emit(StatusAction.CancelAllMessages)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun showActionableMessage(text: String, type: MessageDisplayType, action: MessageAction) {
|
||||||
|
scope.launch {
|
||||||
|
_actions.emit(StatusAction.ShowActionableMessage(text, type, action))
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
package eu.gaudian.translator.utils
|
package eu.gaudian.translator.utils
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import eu.gaudian.translator.model.Language
|
import eu.gaudian.translator.model.Language
|
||||||
import eu.gaudian.translator.model.VocabularyItem
|
import eu.gaudian.translator.model.VocabularyItem
|
||||||
@@ -10,9 +9,9 @@ import eu.gaudian.translator.model.communication.ApiManager
|
|||||||
import eu.gaudian.translator.model.grammar.GrammaticalFeature
|
import eu.gaudian.translator.model.grammar.GrammaticalFeature
|
||||||
import eu.gaudian.translator.model.grammar.LanguageConfig
|
import eu.gaudian.translator.model.grammar.LanguageConfig
|
||||||
import eu.gaudian.translator.model.grammar.VocabularyFeatures
|
import eu.gaudian.translator.model.grammar.VocabularyFeatures
|
||||||
|
import eu.gaudian.translator.model.repository.LanguageConfigRepository
|
||||||
import eu.gaudian.translator.model.repository.SettingsRepository
|
import eu.gaudian.translator.model.repository.SettingsRepository
|
||||||
import eu.gaudian.translator.utils.StringHelper.isSentenceStrict
|
import eu.gaudian.translator.utils.StringHelper.isSentenceStrict
|
||||||
import eu.gaudian.translator.viewmodel.LanguageConfigViewModel
|
|
||||||
import eu.gaudian.translator.viewmodel.MessageDisplayType
|
import eu.gaudian.translator.viewmodel.MessageDisplayType
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
@@ -28,8 +27,7 @@ class VocabularyService(context: Context) {
|
|||||||
|
|
||||||
private val apiRequestHandler = ApiRequestHandler(ApiManager(context), context)
|
private val apiRequestHandler = ApiRequestHandler(ApiManager(context), context)
|
||||||
private val settingsRepository = SettingsRepository(context)
|
private val settingsRepository = SettingsRepository(context)
|
||||||
private val languageConfigViewModel =
|
private val languageConfigRepository = LanguageConfigRepository(context)
|
||||||
LanguageConfigViewModel(context.applicationContext as Application)
|
|
||||||
private val jsonParser = Json { ignoreUnknownKeys = true; isLenient = true }
|
private val jsonParser = Json { ignoreUnknownKeys = true; isLenient = true }
|
||||||
|
|
||||||
suspend fun fetchZipfFrequency(word: String, languageCode: String): Result<Float> = withContext(Dispatchers.IO) {
|
suspend fun fetchZipfFrequency(word: String, languageCode: String): Result<Float> = withContext(Dispatchers.IO) {
|
||||||
@@ -269,7 +267,7 @@ class VocabularyService(context: Context) {
|
|||||||
val wordsWithCategoryOnly = mutableListOf<ClassifiedWord>()
|
val wordsWithCategoryOnly = mutableListOf<ClassifiedWord>()
|
||||||
|
|
||||||
classifiedWords.forEach { word ->
|
classifiedWords.forEach { word ->
|
||||||
val config = languageConfigViewModel.getConfigForLanguage(word.languageCode)
|
val config = languageConfigRepository.getConfigForLanguage(word.languageCode)
|
||||||
val categoryHasFields = config?.categories?.get(word.category)?.fields?.isNotEmpty() ?: false
|
val categoryHasFields = config?.categories?.get(word.category)?.fields?.isNotEmpty() ?: false
|
||||||
if (categoryHasFields) {
|
if (categoryHasFields) {
|
||||||
wordsWithFeaturesToFetch.add(word)
|
wordsWithFeaturesToFetch.add(word)
|
||||||
@@ -283,7 +281,7 @@ class VocabularyService(context: Context) {
|
|||||||
val featureResults = groupedByLangAndCategory.map { (groupKey, words) ->
|
val featureResults = groupedByLangAndCategory.map { (groupKey, words) ->
|
||||||
async {
|
async {
|
||||||
val (langCode, category) = groupKey
|
val (langCode, category) = groupKey
|
||||||
val languageConfig = languageConfigViewModel.getConfigForLanguage(langCode)
|
val languageConfig = languageConfigRepository.getConfigForLanguage(langCode)
|
||||||
fetchFeaturesForGroup(words, languageConfig, category)
|
fetchFeaturesForGroup(words, languageConfig, category)
|
||||||
}
|
}
|
||||||
}.awaitAll().filterNotNull().flatten()
|
}.awaitAll().filterNotNull().flatten()
|
||||||
@@ -329,7 +327,7 @@ class VocabularyService(context: Context) {
|
|||||||
val wordsToClassify = wordsNeedingApi.map {
|
val wordsToClassify = wordsNeedingApi.map {
|
||||||
WordToAnalyze(it.tempId, it.word, languages[it.languageId]?.englishName ?: "")
|
WordToAnalyze(it.tempId, it.word, languages[it.languageId]?.englishName ?: "")
|
||||||
}
|
}
|
||||||
val languageConfigs = languageConfigViewModel.configs.value
|
val languageConfigs = languageConfigRepository.configs.value
|
||||||
val possibleCategories = languageConfigs.values.flatMap { it.categories.keys }.distinct()
|
val possibleCategories = languageConfigs.values.flatMap { it.categories.keys }.distinct()
|
||||||
|
|
||||||
val wordsToClassifyJson = jsonParser.encodeToString(wordsToClassify)
|
val wordsToClassifyJson = jsonParser.encodeToString(wordsToClassify)
|
||||||
@@ -442,7 +440,7 @@ class VocabularyService(context: Context) {
|
|||||||
)
|
)
|
||||||
|
|
||||||
var finalWord = classifiedWord.word
|
var finalWord = classifiedWord.word
|
||||||
val languageConfig = languageConfigViewModel.getConfigForLanguage(classifiedWord.languageCode)
|
val languageConfig = languageConfigRepository.getConfigForLanguage(classifiedWord.languageCode)
|
||||||
val categoryConfig = languageConfig?.categories?.get(newGrammaticalFeature.category)
|
val categoryConfig = languageConfig?.categories?.get(newGrammaticalFeature.category)
|
||||||
|
|
||||||
if (!isSentenceStrict(finalWord)) {
|
if (!isSentenceStrict(finalWord)) {
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
package eu.gaudian.translator.view
|
package eu.gaudian.translator.view
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
@@ -12,9 +11,7 @@ import androidx.compose.animation.slideInHorizontally
|
|||||||
import androidx.compose.animation.slideOutHorizontally
|
import androidx.compose.animation.slideOutHorizontally
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.collectAsState
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import androidx.navigation.NavGraphBuilder
|
import androidx.navigation.NavGraphBuilder
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.NavType
|
import androidx.navigation.NavType
|
||||||
@@ -29,6 +26,7 @@ import eu.gaudian.translator.view.dictionary.EtymologyResultScreen
|
|||||||
import eu.gaudian.translator.view.dictionary.MainDictionaryScreen
|
import eu.gaudian.translator.view.dictionary.MainDictionaryScreen
|
||||||
import eu.gaudian.translator.view.exercises.ExerciseSessionScreen
|
import eu.gaudian.translator.view.exercises.ExerciseSessionScreen
|
||||||
import eu.gaudian.translator.view.exercises.MainExerciseScreen
|
import eu.gaudian.translator.view.exercises.MainExerciseScreen
|
||||||
|
import eu.gaudian.translator.view.exercises.YouTubeBrowserScreen
|
||||||
import eu.gaudian.translator.view.exercises.YouTubeExerciseScreen
|
import eu.gaudian.translator.view.exercises.YouTubeExerciseScreen
|
||||||
import eu.gaudian.translator.view.settings.DictionaryOptionsScreen
|
import eu.gaudian.translator.view.settings.DictionaryOptionsScreen
|
||||||
import eu.gaudian.translator.view.settings.SettingsRoutes
|
import eu.gaudian.translator.view.settings.SettingsRoutes
|
||||||
@@ -46,12 +44,7 @@ import eu.gaudian.translator.view.vocabulary.VocabularyExerciseHostScreen
|
|||||||
import eu.gaudian.translator.view.vocabulary.VocabularyHeatmapScreen
|
import eu.gaudian.translator.view.vocabulary.VocabularyHeatmapScreen
|
||||||
import eu.gaudian.translator.view.vocabulary.VocabularyListScreen
|
import eu.gaudian.translator.view.vocabulary.VocabularyListScreen
|
||||||
import eu.gaudian.translator.view.vocabulary.VocabularySortingScreen
|
import eu.gaudian.translator.view.vocabulary.VocabularySortingScreen
|
||||||
import eu.gaudian.translator.viewmodel.CategoryViewModel
|
|
||||||
import eu.gaudian.translator.viewmodel.ExerciseViewModel
|
|
||||||
import eu.gaudian.translator.viewmodel.ProgressViewModel
|
|
||||||
import okhttp3.internal.platform.PlatformRegistry.applicationContext
|
|
||||||
|
|
||||||
private const val ANIMATION_DURATION = 200
|
|
||||||
private const val TRANSITION_DURATION = 300
|
private const val TRANSITION_DURATION = 300
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -59,8 +52,8 @@ fun AppNavHost(
|
|||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val exerciseViewModel: ExerciseViewModel = viewModel()
|
|
||||||
val progressViewModel: ProgressViewModel = ProgressViewModel.getInstance(applicationContext as Application)
|
|
||||||
|
|
||||||
// 1. Define your Main Tab "Leaf" Routes (the actual start destinations of your graphs)
|
// 1. Define your Main Tab "Leaf" Routes (the actual start destinations of your graphs)
|
||||||
val mainTabRoutes = setOf(
|
val mainTabRoutes = setOf(
|
||||||
@@ -134,8 +127,8 @@ fun AppNavHost(
|
|||||||
// Define all other navigation graphs at the same top level.
|
// Define all other navigation graphs at the same top level.
|
||||||
translationGraph(navController)
|
translationGraph(navController)
|
||||||
dictionaryGraph(navController)
|
dictionaryGraph(navController)
|
||||||
vocabularyGraph(navController, progressViewModel)
|
vocabularyGraph(navController)
|
||||||
exerciseGraph(navController, exerciseViewModel)
|
exerciseGraph(navController)
|
||||||
settingsGraph(navController)
|
settingsGraph(navController)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -190,10 +183,8 @@ fun NavGraphBuilder.dictionaryGraph(navController: NavHostController) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(androidx.compose.animation.ExperimentalAnimationApi::class)
|
|
||||||
fun NavGraphBuilder.vocabularyGraph(
|
fun NavGraphBuilder.vocabularyGraph(
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
progressViewModel: ProgressViewModel
|
|
||||||
) {
|
) {
|
||||||
navigation(
|
navigation(
|
||||||
startDestination = "main_vocabulary",
|
startDestination = "main_vocabulary",
|
||||||
@@ -246,14 +237,12 @@ fun NavGraphBuilder.vocabularyGraph(
|
|||||||
}
|
}
|
||||||
composable("language_progress") {
|
composable("language_progress") {
|
||||||
LanguageProgressScreen(
|
LanguageProgressScreen(
|
||||||
wordsLearned = progressViewModel.totalWordsCompleted.collectAsState().value,
|
|
||||||
navController = navController
|
navController = navController
|
||||||
)
|
)
|
||||||
|
|
||||||
}
|
}
|
||||||
composable("vocabulary_heatmap") {
|
composable("vocabulary_heatmap") {
|
||||||
VocabularyHeatmapScreen(
|
VocabularyHeatmapScreen(
|
||||||
viewModel = progressViewModel,
|
|
||||||
navController = navController,
|
navController = navController,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -358,8 +347,6 @@ fun NavGraphBuilder.vocabularyGraph(
|
|||||||
}
|
}
|
||||||
composable("category_list_screen") {
|
composable("category_list_screen") {
|
||||||
CategoryListScreen(
|
CategoryListScreen(
|
||||||
categoryViewModel = CategoryViewModel.getInstance(applicationContext as Application),
|
|
||||||
progressViewModel = ProgressViewModel.getInstance(applicationContext as Application),
|
|
||||||
onNavigateBack = { navController.popBackStack() },
|
onNavigateBack = { navController.popBackStack() },
|
||||||
onCategoryClicked = { categoryId ->
|
onCategoryClicked = { categoryId ->
|
||||||
navController.navigate("category_detail/$categoryId")
|
navController.navigate("category_detail/$categoryId")
|
||||||
@@ -392,7 +379,6 @@ fun NavGraphBuilder.vocabularyGraph(
|
|||||||
@OptIn(androidx.compose.animation.ExperimentalAnimationApi::class)
|
@OptIn(androidx.compose.animation.ExperimentalAnimationApi::class)
|
||||||
fun NavGraphBuilder.exerciseGraph(
|
fun NavGraphBuilder.exerciseGraph(
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
exerciseViewModel: ExerciseViewModel
|
|
||||||
) {
|
) {
|
||||||
navigation(
|
navigation(
|
||||||
startDestination = "main_exercise",
|
startDestination = "main_exercise",
|
||||||
@@ -401,25 +387,21 @@ fun NavGraphBuilder.exerciseGraph(
|
|||||||
composable("main_exercise") {
|
composable("main_exercise") {
|
||||||
MainExerciseScreen(
|
MainExerciseScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
exerciseViewModel = exerciseViewModel
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable("exercise_session") {
|
composable("exercise_session") {
|
||||||
ExerciseSessionScreen(
|
ExerciseSessionScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
exerciseViewModel = exerciseViewModel
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable("youtube_exercise") {
|
composable("youtube_exercise") {
|
||||||
YouTubeExerciseScreen(
|
YouTubeExerciseScreen(
|
||||||
navController = navController,
|
navController = navController
|
||||||
exerciseViewModel = exerciseViewModel
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
composable("youtube_browse") {
|
composable("youtube_browse") {
|
||||||
eu.gaudian.translator.view.exercises.YouTubeBrowserScreen(
|
YouTubeBrowserScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
exerciseViewModel = exerciseViewModel
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,7 +38,6 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.model.Language
|
import eu.gaudian.translator.model.Language
|
||||||
import eu.gaudian.translator.model.TagCategory
|
import eu.gaudian.translator.model.TagCategory
|
||||||
@@ -57,10 +56,10 @@ enum class DialogCategoryType { TAG, FILTER }
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AddCategoryDialog(
|
fun AddCategoryDialog(
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit
|
||||||
categoryViewModel: CategoryViewModel
|
|
||||||
) {
|
) {
|
||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val categoryViewModel: CategoryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
var categoryName by remember { mutableStateOf("") }
|
var categoryName by remember { mutableStateOf("") }
|
||||||
var selectedLanguages by remember { mutableStateOf<List<Language>>(emptyList()) }
|
var selectedLanguages by remember { mutableStateOf<List<Language>>(emptyList()) }
|
||||||
@@ -160,7 +159,6 @@ fun AddCategoryDialog(
|
|||||||
)
|
)
|
||||||
} else { // Dictionary
|
} else { // Dictionary
|
||||||
DictionarySelectionContent(
|
DictionarySelectionContent(
|
||||||
categoryViewModel = categoryViewModel,
|
|
||||||
selectedPair = selectedDictionaryPair,
|
selectedPair = selectedDictionaryPair,
|
||||||
onPairSelected = { selectedDictionaryPair = it }
|
onPairSelected = { selectedDictionaryPair = it }
|
||||||
)
|
)
|
||||||
@@ -251,11 +249,11 @@ fun AddCategoryDialog(
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun DictionarySelectionContent(
|
private fun DictionarySelectionContent(
|
||||||
categoryViewModel: CategoryViewModel,
|
|
||||||
selectedPair: Pair<Int, Int>?,
|
selectedPair: Pair<Int, Int>?,
|
||||||
onPairSelected: (Pair<Int, Int>) -> Unit
|
onPairSelected: (Pair<Int, Int>) -> Unit
|
||||||
) {
|
) {
|
||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val categoryViewModel: CategoryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
var allDictionaries by remember { mutableStateOf<List<Pair<Language, Language>>>(emptyList()) }
|
var allDictionaries by remember { mutableStateOf<List<Pair<Language, Language>>>(emptyList()) }
|
||||||
var isLoading by remember { mutableStateOf(true) }
|
var isLoading by remember { mutableStateOf(true) }
|
||||||
@@ -334,8 +332,7 @@ private fun DictionarySelectionContent(
|
|||||||
@Composable
|
@Composable
|
||||||
fun AddCategoryDialogPreview() {
|
fun AddCategoryDialogPreview() {
|
||||||
AddCategoryDialog(
|
AddCategoryDialog(
|
||||||
onDismiss = {},
|
onDismiss = {}
|
||||||
categoryViewModel = viewModel()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -344,7 +341,6 @@ fun AddCategoryDialogPreview() {
|
|||||||
fun DictionarySelectionContentPreview() {
|
fun DictionarySelectionContentPreview() {
|
||||||
LocalContext.current
|
LocalContext.current
|
||||||
DictionarySelectionContent(
|
DictionarySelectionContent(
|
||||||
categoryViewModel = viewModel(),
|
|
||||||
selectedPair = Pair(R.string.language_1, R.string.language_2),
|
selectedPair = Pair(R.string.language_1, R.string.language_2),
|
||||||
onPairSelected = {}
|
onPairSelected = {}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package eu.gaudian.translator.view.dialogs
|
package eu.gaudian.translator.view.dialogs
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.animation.fadeIn
|
import androidx.compose.animation.fadeIn
|
||||||
import androidx.compose.animation.fadeOut
|
import androidx.compose.animation.fadeOut
|
||||||
@@ -39,7 +38,6 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.model.VocabularyItem
|
import eu.gaudian.translator.model.VocabularyItem
|
||||||
import eu.gaudian.translator.ui.theme.ThemePreviews
|
import eu.gaudian.translator.ui.theme.ThemePreviews
|
||||||
@@ -51,32 +49,29 @@ import eu.gaudian.translator.view.composable.AppDialog
|
|||||||
import eu.gaudian.translator.view.composable.AppTextField
|
import eu.gaudian.translator.view.composable.AppTextField
|
||||||
import eu.gaudian.translator.view.composable.SourceLanguageDropdown
|
import eu.gaudian.translator.view.composable.SourceLanguageDropdown
|
||||||
import eu.gaudian.translator.view.composable.TargetLanguageDropdown
|
import eu.gaudian.translator.view.composable.TargetLanguageDropdown
|
||||||
import eu.gaudian.translator.viewmodel.CategoryViewModel
|
|
||||||
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
||||||
import eu.gaudian.translator.viewmodel.StatusViewModel
|
import eu.gaudian.translator.viewmodel.StatusViewModel
|
||||||
import eu.gaudian.translator.viewmodel.TranslationViewModel
|
import eu.gaudian.translator.viewmodel.TranslationViewModel
|
||||||
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import okhttp3.internal.platform.PlatformRegistry.applicationContext
|
|
||||||
|
|
||||||
|
|
||||||
enum class VocabularyDialogTab { SINGLE, MULTIPLE, TEXT }
|
enum class VocabularyDialogTab { SINGLE, MULTIPLE, TEXT }
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun AddVocabularyDialog(
|
fun AddVocabularyDialog(
|
||||||
statusViewModel: StatusViewModel,
|
|
||||||
languageViewModel: LanguageViewModel,
|
|
||||||
vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(applicationContext as Application),
|
|
||||||
initialWord: String? = null,
|
initialWord: String? = null,
|
||||||
translation: String? = null,
|
translation: String? = null,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
showMultiple: Boolean = true
|
showMultiple: Boolean = true
|
||||||
) {
|
) {
|
||||||
LocalContext.current
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val statusViewModel: StatusViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
val vocabularyViewModel = hiltViewModel<VocabularyViewModel>(viewModelStoreOwner = activity)
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
val translationViewModel: TranslationViewModel = viewModel()
|
val translationViewModel: TranslationViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val categoryViewModel: CategoryViewModel = viewModel()
|
|
||||||
val connectionConfigured = LocalConnectionConfigured.current
|
val connectionConfigured = LocalConnectionConfigured.current
|
||||||
|
|
||||||
|
|
||||||
@@ -235,6 +230,7 @@ fun AddVocabularyDialog(
|
|||||||
val isGenerating by vocabularyViewModel.isGenerating.collectAsState()
|
val isGenerating by vocabularyViewModel.isGenerating.collectAsState()
|
||||||
val generated by vocabularyViewModel.generatedVocabularyItems.collectAsState()
|
val generated by vocabularyViewModel.generatedVocabularyItems.collectAsState()
|
||||||
LaunchedEffect(isGenerating, generated) {
|
LaunchedEffect(isGenerating, generated) {
|
||||||
|
@Suppress("ControlFlowWithEmptyBody")
|
||||||
if (!isGenerating && showReview) {
|
if (!isGenerating && showReview) {
|
||||||
//TODO think if we still need this
|
//TODO think if we still need this
|
||||||
}
|
}
|
||||||
@@ -341,14 +337,12 @@ fun AddVocabularyDialog(
|
|||||||
) {
|
) {
|
||||||
androidx.compose.material3.Surface(modifier = Modifier.fillMaxSize()) {
|
androidx.compose.material3.Surface(modifier = Modifier.fillMaxSize()) {
|
||||||
VocabularyReviewScreen(
|
VocabularyReviewScreen(
|
||||||
vocabularyViewModel = vocabularyViewModel,
|
|
||||||
onConfirm = { selectedItems, categoryIds ->
|
onConfirm = { selectedItems, categoryIds ->
|
||||||
vocabularyViewModel.addVocabularyItems(selectedItems, categoryIds)
|
vocabularyViewModel.addVocabularyItems(selectedItems, categoryIds)
|
||||||
showReview = false
|
showReview = false
|
||||||
onDismissRequest()
|
onDismissRequest()
|
||||||
},
|
},
|
||||||
onCancel = { showReview = false },
|
onCancel = { showReview = false }
|
||||||
categoryViewModel = categoryViewModel
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -358,14 +352,7 @@ fun AddVocabularyDialog(
|
|||||||
@ThemePreviews
|
@ThemePreviews
|
||||||
@Composable
|
@Composable
|
||||||
fun AddVocabularyDialogPreview() {
|
fun AddVocabularyDialogPreview() {
|
||||||
val activity = LocalContext.current.findActivity()
|
|
||||||
val statusViewModel: StatusViewModel = viewModel()
|
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
|
||||||
val vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(LocalContext.current.applicationContext as Application)
|
|
||||||
|
|
||||||
AddVocabularyDialog(
|
AddVocabularyDialog(
|
||||||
statusViewModel = statusViewModel,
|
|
||||||
languageViewModel = languageViewModel,
|
|
||||||
vocabularyViewModel = vocabularyViewModel,
|
|
||||||
onDismissRequest = {})
|
onDismissRequest = {})
|
||||||
}
|
}
|
||||||
@@ -20,14 +20,16 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
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.utils.findActivity
|
||||||
import eu.gaudian.translator.view.composable.AppButton
|
import eu.gaudian.translator.view.composable.AppButton
|
||||||
import eu.gaudian.translator.view.composable.AppCheckbox
|
import eu.gaudian.translator.view.composable.AppCheckbox
|
||||||
import eu.gaudian.translator.view.composable.AppDropdownMenuItem
|
import eu.gaudian.translator.view.composable.AppDropdownMenuItem
|
||||||
@@ -39,7 +41,6 @@ import eu.gaudian.translator.viewmodel.CategoryViewModel
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CategoryDropdown(
|
fun CategoryDropdown(
|
||||||
categoryViewModel: CategoryViewModel,
|
|
||||||
initialCategoryId: Int? = null,
|
initialCategoryId: Int? = null,
|
||||||
onCategorySelected: (List<VocabularyCategory?>) -> Unit,
|
onCategorySelected: (List<VocabularyCategory?>) -> Unit,
|
||||||
noneSelectable: Boolean? = true,
|
noneSelectable: Boolean? = true,
|
||||||
@@ -47,6 +48,8 @@ fun CategoryDropdown(
|
|||||||
onlyLists: Boolean = false,
|
onlyLists: Boolean = false,
|
||||||
addCategory: Boolean = false
|
addCategory: Boolean = false
|
||||||
) {
|
) {
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val categoryViewModel: CategoryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
var expanded by remember { mutableStateOf(false) }
|
var expanded by remember { mutableStateOf(false) }
|
||||||
val categories by categoryViewModel.categories.collectAsState(initial = emptyList())
|
val categories by categoryViewModel.categories.collectAsState(initial = emptyList())
|
||||||
val selectableCategories = if (onlyLists) categories.filterIsInstance<TagCategory>() else categories
|
val selectableCategories = if (onlyLists) categories.filterIsInstance<TagCategory>() else categories
|
||||||
@@ -229,7 +232,6 @@ fun CategoryDropdown(
|
|||||||
@Composable
|
@Composable
|
||||||
fun CategoryDropdownPreview() {
|
fun CategoryDropdownPreview() {
|
||||||
CategoryDropdown(
|
CategoryDropdown(
|
||||||
categoryViewModel = viewModel(),
|
|
||||||
onCategorySelected = {}
|
onCategorySelected = {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -18,11 +18,9 @@ import eu.gaudian.translator.R
|
|||||||
import eu.gaudian.translator.model.VocabularyCategory
|
import eu.gaudian.translator.model.VocabularyCategory
|
||||||
import eu.gaudian.translator.view.composable.AppDialog
|
import eu.gaudian.translator.view.composable.AppDialog
|
||||||
import eu.gaudian.translator.view.composable.DialogButton
|
import eu.gaudian.translator.view.composable.DialogButton
|
||||||
import eu.gaudian.translator.viewmodel.CategoryViewModel
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CategorySelectionDialog(
|
fun CategorySelectionDialog(
|
||||||
viewModel: CategoryViewModel,
|
|
||||||
onCategorySelected: (List<VocabularyCategory?>) -> Unit,
|
onCategorySelected: (List<VocabularyCategory?>) -> Unit,
|
||||||
onDismissRequest: () -> Unit,
|
onDismissRequest: () -> Unit,
|
||||||
) {
|
) {
|
||||||
@@ -35,7 +33,6 @@ fun CategorySelectionDialog(
|
|||||||
|
|
||||||
|
|
||||||
CategoryDropdown(
|
CategoryDropdown(
|
||||||
categoryViewModel = viewModel,
|
|
||||||
onCategorySelected = { categories ->
|
onCategorySelected = { categories ->
|
||||||
selectedCategory = categories
|
selectedCategory = categories
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,23 +1,26 @@
|
|||||||
package eu.gaudian.translator.view.dialogs
|
package eu.gaudian.translator.view.dialogs
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.material3.TextButton
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.utils.Log
|
import eu.gaudian.translator.utils.Log
|
||||||
|
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.VocabularyViewModel
|
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
||||||
import okhttp3.internal.platform.PlatformRegistry.applicationContext
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun DeleteItemsDialog(
|
fun DeleteItemsDialog(
|
||||||
viewModel: VocabularyViewModel = VocabularyViewModel.getInstance(applicationContext as Application),
|
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
categoryId: Int,
|
categoryId: Int,
|
||||||
) {
|
) {
|
||||||
val categoryVocabularyItemDelete = viewModel.categoryVocabularyItemDelete
|
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
val categoryVocabularyItemDelete = vocabularyViewModel.categoryVocabularyItemDelete
|
||||||
@Suppress("HardCodedStringLiteral")
|
@Suppress("HardCodedStringLiteral")
|
||||||
Log.d("DeleteItemsDialog", "categoryVocabularyItemDelete: $categoryVocabularyItemDelete")
|
Log.d("DeleteItemsDialog", "categoryVocabularyItemDelete: $categoryVocabularyItemDelete")
|
||||||
|
|
||||||
@@ -32,7 +35,7 @@ fun DeleteItemsDialog(
|
|||||||
confirmButton = {
|
confirmButton = {
|
||||||
TextButton(
|
TextButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
viewModel.deleteVocabularyItemsByCategory(categoryId)
|
vocabularyViewModel.deleteVocabularyItemsByCategory(categoryId)
|
||||||
onDismiss()
|
onDismiss()
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package eu.gaudian.translator.view.dialogs
|
package eu.gaudian.translator.view.dialogs
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -25,15 +24,19 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.window.Dialog
|
import androidx.compose.ui.window.Dialog
|
||||||
import androidx.compose.ui.window.DialogProperties
|
import androidx.compose.ui.window.DialogProperties
|
||||||
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.compose.NavHost
|
import androidx.navigation.compose.NavHost
|
||||||
import androidx.navigation.compose.composable
|
import androidx.navigation.compose.composable
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
|
import eu.gaudian.translator.utils.findActivity
|
||||||
import eu.gaudian.translator.view.composable.AppDialog
|
import eu.gaudian.translator.view.composable.AppDialog
|
||||||
import eu.gaudian.translator.view.composable.AppSlider
|
import eu.gaudian.translator.view.composable.AppSlider
|
||||||
import eu.gaudian.translator.view.composable.AppTextField
|
import eu.gaudian.translator.view.composable.AppTextField
|
||||||
@@ -41,17 +44,14 @@ import eu.gaudian.translator.view.composable.DialogButton
|
|||||||
import eu.gaudian.translator.view.composable.SourceLanguageDropdown
|
import eu.gaudian.translator.view.composable.SourceLanguageDropdown
|
||||||
import eu.gaudian.translator.view.composable.TargetLanguageDropdown
|
import eu.gaudian.translator.view.composable.TargetLanguageDropdown
|
||||||
import eu.gaudian.translator.view.hints.getImportVocabularyHint
|
import eu.gaudian.translator.view.hints.getImportVocabularyHint
|
||||||
import eu.gaudian.translator.viewmodel.CategoryViewModel
|
|
||||||
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
||||||
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import okhttp3.internal.platform.PlatformRegistry.applicationContext
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ImportVocabularyDialog(
|
fun ImportVocabularyDialog(
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
languageViewModel: LanguageViewModel,
|
languageViewModel: LanguageViewModel,
|
||||||
categoryViewModel: CategoryViewModel,
|
|
||||||
vocabularyViewModel : VocabularyViewModel,
|
vocabularyViewModel : VocabularyViewModel,
|
||||||
optionalDescription: String? = null,
|
optionalDescription: String? = null,
|
||||||
optionalSearchTerm: String? = null
|
optionalSearchTerm: String? = null
|
||||||
@@ -64,7 +64,6 @@ fun ImportVocabularyDialog(
|
|||||||
navController = navController,
|
navController = navController,
|
||||||
onDismiss = onDismiss,
|
onDismiss = onDismiss,
|
||||||
languageViewModel = languageViewModel,
|
languageViewModel = languageViewModel,
|
||||||
vocabularyViewModel = vocabularyViewModel,
|
|
||||||
optionalDescription = optionalDescription,
|
optionalDescription = optionalDescription,
|
||||||
optionalSearchTerm = optionalSearchTerm
|
optionalSearchTerm = optionalSearchTerm
|
||||||
)
|
)
|
||||||
@@ -78,13 +77,11 @@ fun ImportVocabularyDialog(
|
|||||||
// Full-screen surface to ensure the dialog covers content and stays above the main FAB/menu
|
// Full-screen surface to ensure the dialog covers content and stays above the main FAB/menu
|
||||||
Surface(modifier = Modifier.fillMaxSize()) {
|
Surface(modifier = Modifier.fillMaxSize()) {
|
||||||
VocabularyReviewScreen(
|
VocabularyReviewScreen(
|
||||||
vocabularyViewModel = vocabularyViewModel,
|
|
||||||
onConfirm = { selectedItems, categoryIds ->
|
onConfirm = { selectedItems, categoryIds ->
|
||||||
vocabularyViewModel.addVocabularyItems(selectedItems, categoryIds)
|
vocabularyViewModel.addVocabularyItems(selectedItems, categoryIds)
|
||||||
onDismiss()
|
onDismiss()
|
||||||
},
|
},
|
||||||
onCancel = onDismiss,
|
onCancel = onDismiss
|
||||||
categoryViewModel = categoryViewModel
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -97,10 +94,11 @@ fun ImportDialogContent(
|
|||||||
navController: NavController,
|
navController: NavController,
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
languageViewModel: LanguageViewModel,
|
languageViewModel: LanguageViewModel,
|
||||||
vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(applicationContext as Application),
|
|
||||||
optionalDescription: String? = null,
|
optionalDescription: String? = null,
|
||||||
optionalSearchTerm: String? = null
|
optionalSearchTerm: String? = null
|
||||||
) {
|
) {
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
var category by remember { mutableStateOf(optionalSearchTerm ?: "") }
|
var category by remember { mutableStateOf(optionalSearchTerm ?: "") }
|
||||||
var amount by remember { mutableFloatStateOf(1f) }
|
var amount by remember { mutableFloatStateOf(1f) }
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
@@ -205,4 +203,20 @@ fun ImportDialogContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("HardCodedStringLiteral")
|
||||||
|
@Preview
|
||||||
|
@Composable
|
||||||
|
fun ImportDialogContentPreview() {
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
|
||||||
|
ImportDialogContent(
|
||||||
|
navController = rememberNavController(),
|
||||||
|
onDismiss = {},
|
||||||
|
languageViewModel = languageViewModel,
|
||||||
|
optionalDescription = "Let AI find vocabulary for you",
|
||||||
|
optionalSearchTerm = "Travel"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package eu.gaudian.translator.view.dialogs
|
package eu.gaudian.translator.view.dialogs
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -28,16 +27,12 @@ import eu.gaudian.translator.model.VocabularyStage
|
|||||||
import eu.gaudian.translator.utils.findActivity
|
import eu.gaudian.translator.utils.findActivity
|
||||||
import eu.gaudian.translator.view.composable.AppDialog
|
import eu.gaudian.translator.view.composable.AppDialog
|
||||||
import eu.gaudian.translator.view.composable.MultipleLanguageDropdown
|
import eu.gaudian.translator.view.composable.MultipleLanguageDropdown
|
||||||
import eu.gaudian.translator.viewmodel.CategoryViewModel
|
|
||||||
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
||||||
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import okhttp3.internal.platform.PlatformRegistry.applicationContext
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun StartExerciseDialog(
|
fun StartExerciseDialog(
|
||||||
vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(applicationContext as Application),
|
|
||||||
categoryViewModel: CategoryViewModel = CategoryViewModel.getInstance(applicationContext as Application),
|
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
onConfirm: (
|
onConfirm: (
|
||||||
categories: List<VocabularyCategory>,
|
categories: List<VocabularyCategory>,
|
||||||
@@ -47,6 +42,7 @@ fun StartExerciseDialog(
|
|||||||
) {
|
) {
|
||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
val vocabularyViewModel : VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
var lids by remember { mutableStateOf<List<Int>>(emptyList()) }
|
var lids by remember { mutableStateOf<List<Int>>(emptyList()) }
|
||||||
var languages by remember { mutableStateOf<List<Language>>(emptyList()) }
|
var languages by remember { mutableStateOf<List<Language>>(emptyList()) }
|
||||||
@@ -85,7 +81,6 @@ fun StartExerciseDialog(
|
|||||||
languages
|
languages
|
||||||
)
|
)
|
||||||
CategoryDropdown(
|
CategoryDropdown(
|
||||||
categoryViewModel = categoryViewModel,
|
|
||||||
onCategorySelected = { categories ->
|
onCategorySelected = { categories ->
|
||||||
selectedCategories = categories.filterIsInstance<VocabularyCategory>()
|
selectedCategories = categories.filterIsInstance<VocabularyCategory>()
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -11,26 +11,21 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.utils.findActivity
|
import eu.gaudian.translator.utils.findActivity
|
||||||
import eu.gaudian.translator.view.composable.AppFabMenu
|
import eu.gaudian.translator.view.composable.AppFabMenu
|
||||||
import eu.gaudian.translator.view.composable.AppIcons
|
import eu.gaudian.translator.view.composable.AppIcons
|
||||||
import eu.gaudian.translator.view.composable.FabMenuItem
|
import eu.gaudian.translator.view.composable.FabMenuItem
|
||||||
import eu.gaudian.translator.viewmodel.CategoryViewModel
|
|
||||||
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
||||||
import eu.gaudian.translator.viewmodel.StatusViewModel
|
|
||||||
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun VocabularyMenu(
|
fun VocabularyMenu(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
statusViewModel: StatusViewModel = viewModel(),
|
|
||||||
vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(LocalContext.current.applicationContext as android.app.Application),
|
|
||||||
categoryViewModel: CategoryViewModel = viewModel(),
|
|
||||||
) {
|
) {
|
||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
var showAddVocabularyDialog by remember { mutableStateOf(false) }
|
var showAddVocabularyDialog by remember { mutableStateOf(false) }
|
||||||
var showImportVocabularyDialog by remember { mutableStateOf(false) }
|
var showImportVocabularyDialog by remember { mutableStateOf(false) }
|
||||||
var showAddCategoryDialog by remember { mutableStateOf(false) }
|
var showAddCategoryDialog by remember { mutableStateOf(false) }
|
||||||
@@ -57,9 +52,6 @@ fun VocabularyMenu(
|
|||||||
|
|
||||||
if (showAddVocabularyDialog) {
|
if (showAddVocabularyDialog) {
|
||||||
AddVocabularyDialog(
|
AddVocabularyDialog(
|
||||||
statusViewModel = statusViewModel,
|
|
||||||
languageViewModel = languageViewModel,
|
|
||||||
vocabularyViewModel = vocabularyViewModel,
|
|
||||||
onDismissRequest = { showAddVocabularyDialog = false }
|
onDismissRequest = { showAddVocabularyDialog = false }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -67,7 +59,6 @@ fun VocabularyMenu(
|
|||||||
if (showImportVocabularyDialog) {
|
if (showImportVocabularyDialog) {
|
||||||
ImportVocabularyDialog(
|
ImportVocabularyDialog(
|
||||||
languageViewModel = languageViewModel,
|
languageViewModel = languageViewModel,
|
||||||
categoryViewModel = categoryViewModel,
|
|
||||||
vocabularyViewModel = vocabularyViewModel,
|
vocabularyViewModel = vocabularyViewModel,
|
||||||
onDismiss = { showImportVocabularyDialog = false }
|
onDismiss = { showImportVocabularyDialog = false }
|
||||||
)
|
)
|
||||||
@@ -75,7 +66,6 @@ fun VocabularyMenu(
|
|||||||
|
|
||||||
if (showAddCategoryDialog) {
|
if (showAddCategoryDialog) {
|
||||||
AddCategoryDialog(
|
AddCategoryDialog(
|
||||||
categoryViewModel = categoryViewModel,
|
|
||||||
onDismiss = { showAddCategoryDialog = false }
|
onDismiss = { showAddCategoryDialog = false }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package eu.gaudian.translator.view.dialogs
|
package eu.gaudian.translator.view.dialogs
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
@@ -26,25 +25,25 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.model.VocabularyCategory
|
import eu.gaudian.translator.model.VocabularyCategory
|
||||||
import eu.gaudian.translator.model.VocabularyItem
|
import eu.gaudian.translator.model.VocabularyItem
|
||||||
|
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.AppCheckbox
|
import eu.gaudian.translator.view.composable.AppCheckbox
|
||||||
import eu.gaudian.translator.view.composable.AppScaffold
|
import eu.gaudian.translator.view.composable.AppScaffold
|
||||||
import eu.gaudian.translator.view.composable.AppTopAppBar
|
import eu.gaudian.translator.view.composable.AppTopAppBar
|
||||||
import eu.gaudian.translator.view.hints.getVocabularyReviewHint
|
import eu.gaudian.translator.view.hints.getVocabularyReviewHint
|
||||||
import eu.gaudian.translator.viewmodel.CategoryViewModel
|
|
||||||
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
||||||
import okhttp3.internal.platform.PlatformRegistry.applicationContext
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun VocabularyReviewScreen(
|
fun VocabularyReviewScreen(
|
||||||
vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(applicationContext as Application),
|
|
||||||
categoryViewModel: CategoryViewModel,
|
|
||||||
onConfirm: (List<VocabularyItem>, List<Int>) -> Unit,
|
onConfirm: (List<VocabularyItem>, List<Int>) -> Unit,
|
||||||
onCancel: () -> Unit
|
onCancel: () -> Unit
|
||||||
) {
|
) {
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val vocabularyViewModel : VocabularyViewModel = hiltViewModel(activity)
|
||||||
val generatedItems: List<VocabularyItem> by vocabularyViewModel.generatedVocabularyItems.collectAsState()
|
val generatedItems: List<VocabularyItem> by vocabularyViewModel.generatedVocabularyItems.collectAsState()
|
||||||
val selectedItems = remember { mutableStateListOf<VocabularyItem>() }
|
val selectedItems = remember { mutableStateListOf<VocabularyItem>() }
|
||||||
val duplicates = remember { mutableStateListOf<Boolean>() }
|
val duplicates = remember { mutableStateListOf<Boolean>() }
|
||||||
@@ -129,7 +128,6 @@ fun VocabularyReviewScreen(
|
|||||||
modifier = Modifier.padding(8.dp)
|
modifier = Modifier.padding(8.dp)
|
||||||
)
|
)
|
||||||
CategoryDropdown(
|
CategoryDropdown(
|
||||||
categoryViewModel = categoryViewModel,
|
|
||||||
onCategorySelected = { categories: List<VocabularyCategory?> ->
|
onCategorySelected = { categories: List<VocabularyCategory?> ->
|
||||||
selectedCategoryId = categories.filterNotNull().map { it.id }
|
selectedCategoryId = categories.filterNotNull().map { it.id }
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -31,7 +31,6 @@ import androidx.compose.ui.text.font.FontStyle
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.model.DictionaryEntry
|
import eu.gaudian.translator.model.DictionaryEntry
|
||||||
@@ -50,8 +49,6 @@ import eu.gaudian.translator.view.dialogs.AddVocabularyDialog
|
|||||||
import eu.gaudian.translator.viewmodel.DictionaryViewModel
|
import eu.gaudian.translator.viewmodel.DictionaryViewModel
|
||||||
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
||||||
import eu.gaudian.translator.viewmodel.SettingsViewModel
|
import eu.gaudian.translator.viewmodel.SettingsViewModel
|
||||||
import eu.gaudian.translator.viewmodel.StatusViewModel
|
|
||||||
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
enum class DictionaryResultTab(
|
enum class DictionaryResultTab(
|
||||||
@@ -76,8 +73,7 @@ fun DictionaryResultScreen(
|
|||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
val dictionaryViewModel: DictionaryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val dictionaryViewModel: DictionaryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val vocabularyViewModel: VocabularyViewModel = viewModel(viewModelStoreOwner = activity)
|
|
||||||
val statusViewModel: StatusViewModel = viewModel()
|
|
||||||
val settingsViewModel: SettingsViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val settingsViewModel: SettingsViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
|
||||||
// State Collection
|
// State Collection
|
||||||
@@ -214,9 +210,6 @@ fun DictionaryResultScreen(
|
|||||||
if (showAddVocabularyDialog) {
|
if (showAddVocabularyDialog) {
|
||||||
entryData?.let {
|
entryData?.let {
|
||||||
AddVocabularyDialog(
|
AddVocabularyDialog(
|
||||||
statusViewModel = statusViewModel,
|
|
||||||
languageViewModel = languageViewModel,
|
|
||||||
vocabularyViewModel = vocabularyViewModel,
|
|
||||||
initialWord = it.entry.word,
|
initialWord = it.entry.word,
|
||||||
onDismissRequest = {
|
onDismissRequest = {
|
||||||
@Suppress("AssignedValueIsNeverRead")
|
@Suppress("AssignedValueIsNeverRead")
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
package eu.gaudian.translator.view.dictionary
|
package eu.gaudian.translator.view.dictionary
|
||||||
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import eu.gaudian.translator.utils.findActivity
|
import eu.gaudian.translator.utils.findActivity
|
||||||
@@ -14,10 +14,13 @@ import eu.gaudian.translator.viewmodel.LanguageViewModel
|
|||||||
fun DictionaryScreen(
|
fun DictionaryScreen(
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
onEntryClick: (eu.gaudian.translator.model.DictionaryEntry) -> Unit = {},
|
onEntryClick: (eu.gaudian.translator.model.DictionaryEntry) -> Unit = {},
|
||||||
dictionaryViewModel: DictionaryViewModel,
|
|
||||||
languageViewModel: LanguageViewModel,
|
|
||||||
onNavigateToOptions: () -> Unit
|
onNavigateToOptions: () -> Unit
|
||||||
) {
|
) {
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val dictionaryViewModel: DictionaryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
|
||||||
|
|
||||||
// Use the new refactored component
|
// Use the new refactored component
|
||||||
DictionaryScreenContent(
|
DictionaryScreenContent(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
@@ -31,15 +34,10 @@ fun DictionaryScreen(
|
|||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
fun DictionaryScreenPreview() {
|
fun DictionaryScreenPreview() {
|
||||||
val activity = androidx.compose.ui.platform.LocalContext.current.findActivity()
|
|
||||||
val dictionaryViewModel: DictionaryViewModel = viewModel()
|
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
|
||||||
|
|
||||||
DictionaryScreen(
|
DictionaryScreen(
|
||||||
navController = rememberNavController(),
|
navController = rememberNavController(),
|
||||||
onEntryClick = {},
|
onEntryClick = {},
|
||||||
dictionaryViewModel = dictionaryViewModel,
|
|
||||||
languageViewModel = languageViewModel,
|
|
||||||
onNavigateToOptions = {}
|
onNavigateToOptions = {}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -23,7 +23,6 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
@@ -98,7 +97,7 @@ fun EtymologyScreen(
|
|||||||
@Composable
|
@Composable
|
||||||
fun EtymologyScreenPreview() {
|
fun EtymologyScreenPreview() {
|
||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
val dictionaryViewModel: DictionaryViewModel = viewModel()
|
val dictionaryViewModel: DictionaryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
EtymologyScreen(
|
EtymologyScreen(
|
||||||
navController = rememberNavController(),
|
navController = rememberNavController(),
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ import androidx.compose.ui.text.withStyle
|
|||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.model.Language
|
import eu.gaudian.translator.model.Language
|
||||||
import eu.gaudian.translator.model.grammar.DictionaryEntryData
|
import eu.gaudian.translator.model.grammar.DictionaryEntryData
|
||||||
@@ -71,6 +71,7 @@ import eu.gaudian.translator.model.grammar.UnifiedMorphology
|
|||||||
import eu.gaudian.translator.model.grammar.UnifiedMorphologyParser
|
import eu.gaudian.translator.model.grammar.UnifiedMorphologyParser
|
||||||
import eu.gaudian.translator.utils.dictionary.LocalDictionaryWordInfo
|
import eu.gaudian.translator.utils.dictionary.LocalDictionaryWordInfo
|
||||||
import eu.gaudian.translator.utils.dictionary.PartOfSpeechTranslator
|
import eu.gaudian.translator.utils.dictionary.PartOfSpeechTranslator
|
||||||
|
import eu.gaudian.translator.utils.findActivity
|
||||||
import eu.gaudian.translator.view.composable.AppIcons
|
import eu.gaudian.translator.view.composable.AppIcons
|
||||||
import eu.gaudian.translator.view.composable.AutoResizeSingleLineText
|
import eu.gaudian.translator.view.composable.AutoResizeSingleLineText
|
||||||
import eu.gaudian.translator.viewmodel.DictionaryViewModel
|
import eu.gaudian.translator.viewmodel.DictionaryViewModel
|
||||||
@@ -92,7 +93,8 @@ fun RichLocalEntryDisplay(
|
|||||||
expandable: Boolean = true, // Enables whole-entry collapsing
|
expandable: Boolean = true, // Enables whole-entry collapsing
|
||||||
initiallyExpanded: Boolean = true // Default state
|
initiallyExpanded: Boolean = true // Default state
|
||||||
) {
|
) {
|
||||||
val languageConfigViewModel: LanguageConfigViewModel = viewModel()
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val languageConfigViewModel: LanguageConfigViewModel = hiltViewModel(activity)
|
||||||
val allConfigs by languageConfigViewModel.configs.collectAsState()
|
val allConfigs by languageConfigViewModel.configs.collectAsState()
|
||||||
val languageConfig = allConfigs[langCode]
|
val languageConfig = allConfigs[langCode]
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
package eu.gaudian.translator.view.dictionary
|
package eu.gaudian.translator.view.dictionary
|
||||||
|
|
||||||
import androidx.activity.compose.LocalActivity
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@@ -11,11 +10,8 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalInspectionMode
|
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.ViewModelStoreOwner
|
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
@@ -46,17 +42,12 @@ private data class DictionaryTab(override val title: String, override val icon:
|
|||||||
fun MainDictionaryScreen(
|
fun MainDictionaryScreen(
|
||||||
navController: NavController
|
navController: NavController
|
||||||
) {
|
) {
|
||||||
val viewModelStoreOwner = if (LocalInspectionMode.current) {
|
|
||||||
null
|
|
||||||
} else {
|
|
||||||
LocalActivity.current
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
|
|
||||||
val dictionaryViewModel: DictionaryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val dictionaryViewModel: DictionaryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val correctionViewModel: CorrectionViewModel = viewModel(viewModelStoreOwner = viewModelStoreOwner as ViewModelStoreOwner)
|
val correctionViewModel: CorrectionViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val dictionaryTabs = getDictionaryTabs()
|
val dictionaryTabs = getDictionaryTabs()
|
||||||
|
|
||||||
@@ -78,8 +69,6 @@ fun MainDictionaryScreen(
|
|||||||
when (selectedTab) {
|
when (selectedTab) {
|
||||||
dictionaryTabs[0] -> DictionaryScreen(
|
dictionaryTabs[0] -> DictionaryScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
dictionaryViewModel = dictionaryViewModel,
|
|
||||||
languageViewModel = languageViewModel,
|
|
||||||
onEntryClick = { entry ->
|
onEntryClick = { entry ->
|
||||||
// Set flag indicating navigation is from external source (not DictionaryResultScreen)
|
// Set flag indicating navigation is from external source (not DictionaryResultScreen)
|
||||||
dictionaryViewModel.setNavigatingFromDictionaryResult(false)
|
dictionaryViewModel.setNavigatingFromDictionaryResult(false)
|
||||||
|
|||||||
@@ -22,11 +22,13 @@ import androidx.compose.runtime.collectAsState
|
|||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.model.Exercise
|
import eu.gaudian.translator.model.Exercise
|
||||||
|
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.AppCard
|
import eu.gaudian.translator.view.composable.AppCard
|
||||||
import eu.gaudian.translator.view.composable.AppIcons
|
import eu.gaudian.translator.view.composable.AppIcons
|
||||||
@@ -40,7 +42,8 @@ fun ExerciseListScreen(
|
|||||||
onLongExerciseClicked: (Exercise) -> Unit,
|
onLongExerciseClicked: (Exercise) -> Unit,
|
||||||
onDeleteClicked: (Exercise) -> Unit,
|
onDeleteClicked: (Exercise) -> Unit,
|
||||||
) {
|
) {
|
||||||
val exerciseViewModel: ExerciseViewModel = viewModel()
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val exerciseViewModel: ExerciseViewModel = hiltViewModel(activity)
|
||||||
val exercises by exerciseViewModel.exercises.collectAsState()
|
val exercises by exerciseViewModel.exercises.collectAsState()
|
||||||
|
|
||||||
AppOutlinedCard{
|
AppOutlinedCard{
|
||||||
|
|||||||
@@ -33,11 +33,12 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
@@ -52,6 +53,7 @@ import eu.gaudian.translator.model.VocabularyTestQuestion
|
|||||||
import eu.gaudian.translator.model.WordOrderQuestion
|
import eu.gaudian.translator.model.WordOrderQuestion
|
||||||
import eu.gaudian.translator.ui.theme.ThemePreviews
|
import eu.gaudian.translator.ui.theme.ThemePreviews
|
||||||
import eu.gaudian.translator.ui.theme.semanticColors
|
import eu.gaudian.translator.ui.theme.semanticColors
|
||||||
|
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
|
||||||
import eu.gaudian.translator.view.composable.ComponentDefaults
|
import eu.gaudian.translator.view.composable.ComponentDefaults
|
||||||
@@ -65,9 +67,11 @@ import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
|||||||
@Composable
|
@Composable
|
||||||
fun ExerciseSessionScreen(
|
fun ExerciseSessionScreen(
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
exerciseViewModel: ExerciseViewModel, // Changed: No longer creates its own instance
|
|
||||||
vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(androidx.compose.ui.platform.LocalContext.current.applicationContext as android.app.Application)
|
|
||||||
) {
|
) {
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
val exerciseViewModel: ExerciseViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
|
||||||
val sessionState by exerciseViewModel.exerciseSessionState.collectAsState()
|
val sessionState by exerciseViewModel.exerciseSessionState.collectAsState()
|
||||||
|
|
||||||
var showVocabulary by remember { mutableStateOf(true) }
|
var showVocabulary by remember { mutableStateOf(true) }
|
||||||
@@ -267,7 +271,9 @@ fun ExerciseQuestionScreen(
|
|||||||
onCloseClick: () -> Unit,
|
onCloseClick: () -> Unit,
|
||||||
navController: NavController
|
navController: NavController
|
||||||
) {
|
) {
|
||||||
val exerciseViewModel = viewModel<ExerciseViewModel>()
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val exerciseViewModel = hiltViewModel<ExerciseViewModel>(viewModelStoreOwner = activity)
|
||||||
|
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
|
|||||||
@@ -24,11 +24,14 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
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.model.Exercise
|
import eu.gaudian.translator.model.Exercise
|
||||||
|
import eu.gaudian.translator.utils.findActivity
|
||||||
import eu.gaudian.translator.view.composable.AppAlertDialog
|
import eu.gaudian.translator.view.composable.AppAlertDialog
|
||||||
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
|
||||||
@@ -52,13 +55,13 @@ enum class ExerciseTab(override val title: String, override val icon: ImageVecto
|
|||||||
* showing a tabbed layout for the dashboard and the list of all exercises.
|
* showing a tabbed layout for the dashboard and the list of all exercises.
|
||||||
*
|
*
|
||||||
* @param navController The main NavController to handle navigation to an exercise session.
|
* @param navController The main NavController to handle navigation to an exercise session.
|
||||||
* @param exerciseViewModel The ViewModel for managing exercise data and state.
|
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun MainExerciseScreen(
|
fun MainExerciseScreen(
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
exerciseViewModel: ExerciseViewModel // Changed: No longer creates its own instance
|
|
||||||
) {
|
) {
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val exerciseViewModel : ExerciseViewModel = hiltViewModel(activity)
|
||||||
val aiState by exerciseViewModel.aiGenerationState.collectAsState()
|
val aiState by exerciseViewModel.aiGenerationState.collectAsState()
|
||||||
var exerciseToDelete by remember { mutableStateOf<Exercise?>(null) }
|
var exerciseToDelete by remember { mutableStateOf<Exercise?>(null) }
|
||||||
var showGenerateDialog by remember { mutableStateOf(false) }
|
var showGenerateDialog by remember { mutableStateOf(false) }
|
||||||
|
|||||||
@@ -29,8 +29,10 @@ import androidx.compose.ui.platform.LocalContext
|
|||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
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.findActivity
|
||||||
import eu.gaudian.translator.view.composable.AppIcons
|
import eu.gaudian.translator.view.composable.AppIcons
|
||||||
import eu.gaudian.translator.view.composable.AppScaffold
|
import eu.gaudian.translator.view.composable.AppScaffold
|
||||||
import eu.gaudian.translator.view.composable.AppTopAppBar
|
import eu.gaudian.translator.view.composable.AppTopAppBar
|
||||||
@@ -45,9 +47,11 @@ import java.net.URLDecoder
|
|||||||
@Composable
|
@Composable
|
||||||
@SuppressLint("SetJavaScriptEnabled")
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
fun YouTubeBrowserScreen(
|
fun YouTubeBrowserScreen(
|
||||||
navController: NavController,
|
navController: NavController
|
||||||
exerciseViewModel: ExerciseViewModel
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val exerciseViewModel : ExerciseViewModel = hiltViewModel(activity)
|
||||||
var pendingVideoUrl by remember { mutableStateOf<String?>(null) }
|
var pendingVideoUrl by remember { mutableStateOf<String?>(null) }
|
||||||
var showLanguageDialog by remember { mutableStateOf(false) }
|
var showLanguageDialog by remember { mutableStateOf(false) }
|
||||||
var showCustomDialog by remember { mutableStateOf(false) }
|
var showCustomDialog by remember { mutableStateOf(false) }
|
||||||
|
|||||||
@@ -37,6 +37,7 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.viewinterop.AndroidView
|
import androidx.compose.ui.viewinterop.AndroidView
|
||||||
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.YouTubePlayer
|
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.YouTubePlayer
|
||||||
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.AbstractYouTubePlayerListener
|
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.listeners.AbstractYouTubePlayerListener
|
||||||
@@ -44,6 +45,7 @@ import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.options.IFram
|
|||||||
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.utils.loadOrCueVideo
|
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.utils.loadOrCueVideo
|
||||||
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView
|
import com.pierfrancescosoffritti.androidyoutubeplayer.core.player.views.YouTubePlayerView
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
|
import eu.gaudian.translator.utils.findActivity
|
||||||
import eu.gaudian.translator.view.composable.AppIcons
|
import eu.gaudian.translator.view.composable.AppIcons
|
||||||
import eu.gaudian.translator.view.composable.AppScaffold
|
import eu.gaudian.translator.view.composable.AppScaffold
|
||||||
import eu.gaudian.translator.view.composable.AppTopAppBar
|
import eu.gaudian.translator.view.composable.AppTopAppBar
|
||||||
@@ -53,9 +55,10 @@ import eu.gaudian.translator.viewmodel.YouTubeExerciseState
|
|||||||
@SuppressLint("SetJavaScriptEnabled")
|
@SuppressLint("SetJavaScriptEnabled")
|
||||||
@Composable
|
@Composable
|
||||||
fun YouTubeExerciseScreen(
|
fun YouTubeExerciseScreen(
|
||||||
navController: NavController,
|
navController: NavController
|
||||||
exerciseViewModel: ExerciseViewModel
|
|
||||||
) {
|
) {
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val exerciseViewModel : ExerciseViewModel = hiltViewModel(activity)
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val lifecycleOwner = androidx.lifecycle.compose.LocalLifecycleOwner.current
|
val lifecycleOwner = androidx.lifecycle.compose.LocalLifecycleOwner.current
|
||||||
val sessionParams by exerciseViewModel.youTubeSessionParams.collectAsState()
|
val sessionParams by exerciseViewModel.youTubeSessionParams.collectAsState()
|
||||||
|
|||||||
@@ -40,7 +40,6 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import eu.gaudian.translator.BuildConfig
|
import eu.gaudian.translator.BuildConfig
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
@@ -370,8 +369,9 @@ private fun DeveloperOptions(
|
|||||||
navController: NavController,
|
navController: NavController,
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val statusViewModel: StatusViewModel = viewModel()
|
|
||||||
val activity = context.findActivity()
|
val activity = context.findActivity()
|
||||||
|
val statusViewModel: StatusViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val settingsViewModel: SettingsViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val settingsViewModel: SettingsViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -59,10 +59,11 @@ import kotlin.math.roundToInt
|
|||||||
@Composable
|
@Composable
|
||||||
fun VocabularyProgressOptionsScreen(
|
fun VocabularyProgressOptionsScreen(
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(LocalContext.current.applicationContext as android.app.Application)
|
|
||||||
) {
|
) {
|
||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
val settingsViewModel: SettingsViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val settingsViewModel: SettingsViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val exportFileLauncher = rememberLauncherForActivityResult(
|
val exportFileLauncher = rememberLauncherForActivityResult(
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
package eu.gaudian.translator.view.settings
|
package eu.gaudian.translator.view.settings
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@@ -53,15 +52,17 @@ import eu.gaudian.translator.view.composable.SingleLanguageDropDown
|
|||||||
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
||||||
import eu.gaudian.translator.viewmodel.StatusViewModel
|
import eu.gaudian.translator.viewmodel.StatusViewModel
|
||||||
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
||||||
import okhttp3.internal.platform.PlatformRegistry.applicationContext
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun VocabularyRepositoryOptionsScreen(
|
fun VocabularyRepositoryOptionsScreen(
|
||||||
navController: NavController,
|
navController: NavController
|
||||||
vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(applicationContext as Application),
|
|
||||||
statusViewModel: StatusViewModel = StatusViewModel.getInstance(applicationContext as Application)
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
val statusViewModel: StatusViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
|
||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val repositoryStateImportedFrom = stringResource(R.string.repository_state_imported_from)
|
val repositoryStateImportedFrom = stringResource(R.string.repository_state_imported_from)
|
||||||
|
|
||||||
@@ -79,7 +80,6 @@ fun VocabularyRepositoryOptionsScreen(
|
|||||||
)
|
)
|
||||||
|
|
||||||
// CSV/Excel import state
|
// CSV/Excel import state
|
||||||
val activity = LocalContext.current.findActivity()
|
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val showTableImportDialog = remember { mutableStateOf(false) }
|
val showTableImportDialog = remember { mutableStateOf(false) }
|
||||||
var parsedTable by remember { mutableStateOf<List<List<String>>>(emptyList()) }
|
var parsedTable by remember { mutableStateOf<List<List<String>>>(emptyList()) }
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
package eu.gaudian.translator.view.translation
|
package eu.gaudian.translator.view.translation
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import android.content.ClipData
|
import android.content.ClipData
|
||||||
import android.content.ClipboardManager
|
import android.content.ClipboardManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
@@ -64,16 +63,13 @@ import eu.gaudian.translator.view.hints.TranslationScreenHint
|
|||||||
import eu.gaudian.translator.view.settings.SettingsRoutes
|
import eu.gaudian.translator.view.settings.SettingsRoutes
|
||||||
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
||||||
import eu.gaudian.translator.viewmodel.SettingsViewModel
|
import eu.gaudian.translator.viewmodel.SettingsViewModel
|
||||||
import eu.gaudian.translator.viewmodel.StatusViewModel
|
|
||||||
import eu.gaudian.translator.viewmodel.TranslationViewModel
|
import eu.gaudian.translator.viewmodel.TranslationViewModel
|
||||||
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TranslationScreen(
|
fun TranslationScreen(
|
||||||
translationViewModel: TranslationViewModel,
|
translationViewModel: TranslationViewModel,
|
||||||
languageViewModel: LanguageViewModel,
|
languageViewModel: LanguageViewModel,
|
||||||
statusViewModel: StatusViewModel,
|
|
||||||
onHistoryClick: () -> Unit,
|
onHistoryClick: () -> Unit,
|
||||||
onSettingsClick: () -> Unit,
|
onSettingsClick: () -> Unit,
|
||||||
navController: NavHostController
|
navController: NavHostController
|
||||||
@@ -82,10 +78,6 @@ fun TranslationScreen(
|
|||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
val settingsViewModel: SettingsViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val settingsViewModel: SettingsViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
|
||||||
val vocabularyViewModel = remember(context) {
|
|
||||||
VocabularyViewModel.getInstance(context.applicationContext as Application)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val isInitializationComplete by settingsViewModel.isInitialized.collectAsStateWithLifecycle(
|
val isInitializationComplete by settingsViewModel.isInitialized.collectAsStateWithLifecycle(
|
||||||
initialValue = true,
|
initialValue = true,
|
||||||
@@ -111,9 +103,7 @@ fun TranslationScreen(
|
|||||||
LoadedTranslationContent(
|
LoadedTranslationContent(
|
||||||
translationViewModel = translationViewModel,
|
translationViewModel = translationViewModel,
|
||||||
languageViewModel = languageViewModel,
|
languageViewModel = languageViewModel,
|
||||||
statusViewModel = statusViewModel,
|
|
||||||
settingsViewModel = settingsViewModel,
|
settingsViewModel = settingsViewModel,
|
||||||
vocabularyViewModel = vocabularyViewModel,
|
|
||||||
onHistoryClick = onHistoryClick,
|
onHistoryClick = onHistoryClick,
|
||||||
onSettingsClick = onSettingsClick,
|
onSettingsClick = onSettingsClick,
|
||||||
context = context
|
context = context
|
||||||
@@ -126,9 +116,7 @@ fun TranslationScreen(
|
|||||||
private fun LoadedTranslationContent(
|
private fun LoadedTranslationContent(
|
||||||
translationViewModel: TranslationViewModel,
|
translationViewModel: TranslationViewModel,
|
||||||
languageViewModel: LanguageViewModel,
|
languageViewModel: LanguageViewModel,
|
||||||
statusViewModel: StatusViewModel,
|
|
||||||
settingsViewModel: SettingsViewModel,
|
settingsViewModel: SettingsViewModel,
|
||||||
vocabularyViewModel: VocabularyViewModel,
|
|
||||||
onHistoryClick: () -> Unit,
|
onHistoryClick: () -> Unit,
|
||||||
onSettingsClick: () -> Unit,
|
onSettingsClick: () -> Unit,
|
||||||
context: Context
|
context: Context
|
||||||
@@ -163,9 +151,6 @@ private fun LoadedTranslationContent(
|
|||||||
|
|
||||||
if (showAddVocabularyDialog) {
|
if (showAddVocabularyDialog) {
|
||||||
AddVocabularyDialog(
|
AddVocabularyDialog(
|
||||||
statusViewModel = statusViewModel,
|
|
||||||
languageViewModel = languageViewModel,
|
|
||||||
vocabularyViewModel = vocabularyViewModel,
|
|
||||||
initialWord = inputText,
|
initialWord = inputText,
|
||||||
translation = translatedText,
|
translation = translatedText,
|
||||||
onDismissRequest = { showAddVocabularyDialog = false },
|
onDismissRequest = { showAddVocabularyDialog = false },
|
||||||
|
|||||||
@@ -11,14 +11,12 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import eu.gaudian.translator.utils.TextToSpeechHelper
|
import eu.gaudian.translator.utils.TextToSpeechHelper
|
||||||
import eu.gaudian.translator.utils.findActivity
|
import eu.gaudian.translator.utils.findActivity
|
||||||
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
||||||
import eu.gaudian.translator.viewmodel.SettingsViewModel
|
import eu.gaudian.translator.viewmodel.SettingsViewModel
|
||||||
import eu.gaudian.translator.viewmodel.StatusViewModel
|
|
||||||
import eu.gaudian.translator.viewmodel.TranslationViewModel
|
import eu.gaudian.translator.viewmodel.TranslationViewModel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
|
||||||
@@ -26,11 +24,10 @@ import kotlinx.coroutines.launch
|
|||||||
@Composable
|
@Composable
|
||||||
fun TranslationScreen(
|
fun TranslationScreen(
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
statusViewModel: StatusViewModel = viewModel(),
|
|
||||||
translationViewModel: TranslationViewModel = viewModel(),
|
|
||||||
) {
|
) {
|
||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
val translationViewModel: TranslationViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
var showHistorySheet by remember { mutableStateOf(false) }
|
var showHistorySheet by remember { mutableStateOf(false) }
|
||||||
val sheetState = rememberModalBottomSheetState()
|
val sheetState = rememberModalBottomSheetState()
|
||||||
|
|
||||||
@@ -42,7 +39,6 @@ fun TranslationScreen(
|
|||||||
TranslationScreen(
|
TranslationScreen(
|
||||||
translationViewModel = translationViewModel,
|
translationViewModel = translationViewModel,
|
||||||
languageViewModel = languageViewModel,
|
languageViewModel = languageViewModel,
|
||||||
statusViewModel = statusViewModel,
|
|
||||||
onHistoryClick = { showHistorySheet = true },
|
onHistoryClick = { showHistorySheet = true },
|
||||||
onSettingsClick = {
|
onSettingsClick = {
|
||||||
@Suppress("HardCodedStringLiteral")
|
@Suppress("HardCodedStringLiteral")
|
||||||
@@ -92,7 +88,5 @@ fun TranslationScreenPreview() {
|
|||||||
val navController = NavController(LocalContext.current)
|
val navController = NavController(LocalContext.current)
|
||||||
TranslationScreen(
|
TranslationScreen(
|
||||||
navController = navController,
|
navController = navController,
|
||||||
statusViewModel = viewModel(),
|
|
||||||
translationViewModel = viewModel(),
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -33,7 +33,6 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.model.TagCategory
|
import eu.gaudian.translator.model.TagCategory
|
||||||
@@ -63,10 +62,10 @@ fun CategoryDetailScreen(
|
|||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
val categoryViewModel: CategoryViewModel = viewModel(viewModelStoreOwner = activity)
|
val categoryViewModel: CategoryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val progressViewModel: ProgressViewModel = ProgressViewModel.getInstance(LocalContext.current.applicationContext as android.app.Application)
|
val progressViewModel: ProgressViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(activity.application as android.app.Application)
|
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
|
||||||
val category by categoryViewModel.getCategoryById(categoryId).collectAsState(initial = null)
|
val category by categoryViewModel.getCategoryById(categoryId).collectAsState(initial = null)
|
||||||
val categoryProgressList by progressViewModel.categoryProgressList.collectAsState()
|
val categoryProgressList by progressViewModel.categoryProgressList.collectAsState()
|
||||||
@@ -254,7 +253,6 @@ fun CategoryDetailScreen(
|
|||||||
if (showDeleteItemsDialog) {
|
if (showDeleteItemsDialog) {
|
||||||
DeleteItemsDialog(
|
DeleteItemsDialog(
|
||||||
onDismiss = { categoryViewModel.setShowDeleteItemsDialog(false, categoryId) },
|
onDismiss = { categoryViewModel.setShowDeleteItemsDialog(false, categoryId) },
|
||||||
viewModel = vocabularyViewModel,
|
|
||||||
categoryId = categoryId
|
categoryId = categoryId
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,11 +31,14 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
import androidx.compose.ui.hapticfeedback.HapticFeedbackType
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.platform.LocalHapticFeedback
|
import androidx.compose.ui.platform.LocalHapticFeedback
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.model.TagCategory
|
import eu.gaudian.translator.model.TagCategory
|
||||||
|
import eu.gaudian.translator.utils.findActivity
|
||||||
import eu.gaudian.translator.view.composable.AppIcons
|
import eu.gaudian.translator.view.composable.AppIcons
|
||||||
import eu.gaudian.translator.view.composable.AppScaffold
|
import eu.gaudian.translator.view.composable.AppScaffold
|
||||||
import eu.gaudian.translator.view.composable.AppTopAppBar
|
import eu.gaudian.translator.view.composable.AppTopAppBar
|
||||||
@@ -57,12 +60,16 @@ enum class SortOption {
|
|||||||
@OptIn(ExperimentalFoundationApi::class)
|
@OptIn(ExperimentalFoundationApi::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun CategoryListScreen(
|
fun CategoryListScreen(
|
||||||
categoryViewModel: CategoryViewModel,
|
|
||||||
progressViewModel: ProgressViewModel,
|
|
||||||
onNavigateBack: () -> Unit,
|
onNavigateBack: () -> Unit,
|
||||||
onCategoryClicked: (Int) -> Unit,
|
onCategoryClicked: (Int) -> Unit,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val categoryViewModel : CategoryViewModel = hiltViewModel(activity)
|
||||||
|
val progressViewModel : ProgressViewModel = hiltViewModel(activity)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var sortOption by remember { mutableStateOf(SortOption.NAME) }
|
var sortOption by remember { mutableStateOf(SortOption.NAME) }
|
||||||
var sortMenuExpanded by remember { mutableStateOf(false) }
|
var sortMenuExpanded by remember { mutableStateOf(false) }
|
||||||
val categoryProgressList by progressViewModel.categoryProgressList.collectAsState(initial = emptyList())
|
val categoryProgressList by progressViewModel.categoryProgressList.collectAsState(initial = emptyList())
|
||||||
@@ -75,7 +82,6 @@ fun CategoryListScreen(
|
|||||||
if (showAddCategoryDialog) {
|
if (showAddCategoryDialog) {
|
||||||
AddCategoryDialog(
|
AddCategoryDialog(
|
||||||
onDismiss = { showAddCategoryDialog = false },
|
onDismiss = { showAddCategoryDialog = false },
|
||||||
categoryViewModel = categoryViewModel,
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
package eu.gaudian.translator.view.vocabulary
|
package eu.gaudian.translator.view.vocabulary
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
import android.annotation.SuppressLint
|
||||||
import android.app.Application
|
|
||||||
import androidx.compose.animation.animateContentSize
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.animation.core.Animatable
|
import androidx.compose.animation.core.Animatable
|
||||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||||
@@ -89,14 +88,15 @@ fun DashboardContent(
|
|||||||
onNavigateToCategoryDetail: (Int) -> Unit,
|
onNavigateToCategoryDetail: (Int) -> Unit,
|
||||||
onNavigateToCategoryList: () -> Unit,
|
onNavigateToCategoryList: () -> Unit,
|
||||||
onShowWordPairExerciseDialog: () -> Unit,
|
onShowWordPairExerciseDialog: () -> Unit,
|
||||||
vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(LocalContext.current.applicationContext as Application),
|
|
||||||
progressViewModel: ProgressViewModel = ProgressViewModel.getInstance(LocalContext.current.applicationContext as Application),
|
|
||||||
) {
|
) {
|
||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val settingsViewModel: SettingsViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val settingsViewModel: SettingsViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
val progressViewModel: ProgressViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
|
||||||
var showMissingLanguageDialog by remember { mutableStateOf(false) }
|
var showMissingLanguageDialog by remember { mutableStateOf(false) }
|
||||||
var selectedMissingLanguageId by remember { mutableStateOf<Int?>(null) }
|
var selectedMissingLanguageId by remember { mutableStateOf<Int?>(null) }
|
||||||
|
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
|
||||||
val affectedItems by remember(selectedMissingLanguageId) {
|
val affectedItems by remember(selectedMissingLanguageId) {
|
||||||
selectedMissingLanguageId?.let {
|
selectedMissingLanguageId?.let {
|
||||||
@@ -630,7 +630,6 @@ private fun LazyStatusWidget(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
StatusWidget(
|
StatusWidget(
|
||||||
vocabularyViewModel = vocabularyViewModel,
|
|
||||||
onNavigateToNew = onNavigateToNew,
|
onNavigateToNew = onNavigateToNew,
|
||||||
onNavigateToDuplicates = onNavigateToDuplicates,
|
onNavigateToDuplicates = onNavigateToDuplicates,
|
||||||
onNavigateToFaulty = onNavigateToFaulty,
|
onNavigateToFaulty = onNavigateToFaulty,
|
||||||
|
|||||||
@@ -34,6 +34,7 @@ import androidx.compose.material3.MaterialTheme
|
|||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
@@ -45,6 +46,7 @@ import androidx.compose.ui.draw.scale
|
|||||||
import androidx.compose.ui.geometry.Offset
|
import androidx.compose.ui.geometry.Offset
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.StrokeCap
|
import androidx.compose.ui.graphics.StrokeCap
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
@@ -52,17 +54,27 @@ import androidx.compose.ui.text.style.TextAlign
|
|||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
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.model.LanguageLevels
|
import eu.gaudian.translator.model.LanguageLevels
|
||||||
import eu.gaudian.translator.model.MyAppLanguageLevel
|
import eu.gaudian.translator.model.MyAppLanguageLevel
|
||||||
|
import eu.gaudian.translator.utils.findActivity
|
||||||
import eu.gaudian.translator.view.composable.AppDialog
|
import eu.gaudian.translator.view.composable.AppDialog
|
||||||
import eu.gaudian.translator.view.composable.AppIcons
|
import eu.gaudian.translator.view.composable.AppIcons
|
||||||
import eu.gaudian.translator.view.composable.AppScaffold
|
import eu.gaudian.translator.view.composable.AppScaffold
|
||||||
import eu.gaudian.translator.view.composable.AppTopAppBar
|
import eu.gaudian.translator.view.composable.AppTopAppBar
|
||||||
|
import eu.gaudian.translator.viewmodel.ProgressViewModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun LanguageProgressScreen(wordsLearned: Int, navController: NavController) {
|
fun LanguageProgressScreen(navController: NavController) {
|
||||||
|
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val progressViewModel : ProgressViewModel = hiltViewModel(activity)
|
||||||
|
|
||||||
|
val wordsLearned = progressViewModel.totalWordsCompleted.collectAsState().value
|
||||||
|
|
||||||
|
|
||||||
AppScaffold(
|
AppScaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
AppTopAppBar(
|
AppTopAppBar(
|
||||||
@@ -368,5 +380,5 @@ private fun LevelDetailDialog(level: MyAppLanguageLevel, onDismiss: () -> Unit)
|
|||||||
@Preview(showBackground = true)
|
@Preview(showBackground = true)
|
||||||
@Composable
|
@Composable
|
||||||
fun LanguageProgressScreenPreview() {
|
fun LanguageProgressScreenPreview() {
|
||||||
LanguageProgressScreen(wordsLearned = 50000, navController = NavController(androidx.compose.ui.platform.LocalContext.current))
|
LanguageProgressScreen(navController = NavController(LocalContext.current))
|
||||||
}
|
}
|
||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
package eu.gaudian.translator.view.vocabulary
|
package eu.gaudian.translator.view.vocabulary
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.LocalActivity
|
import androidx.activity.compose.LocalActivity
|
||||||
import androidx.compose.animation.core.animateDpAsState
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
@@ -34,7 +33,7 @@ import androidx.compose.ui.layout.onSizeChanged
|
|||||||
import androidx.compose.ui.platform.LocalDensity
|
import androidx.compose.ui.platform.LocalDensity
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavGraph.Companion.findStartDestination
|
import androidx.navigation.NavGraph.Companion.findStartDestination
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
@@ -59,7 +58,6 @@ import eu.gaudian.translator.view.composable.OptionItemSwitch
|
|||||||
import eu.gaudian.translator.view.composable.TabItem
|
import eu.gaudian.translator.view.composable.TabItem
|
||||||
import eu.gaudian.translator.view.dialogs.StartExerciseDialog
|
import eu.gaudian.translator.view.dialogs.StartExerciseDialog
|
||||||
import eu.gaudian.translator.view.dialogs.VocabularyMenu
|
import eu.gaudian.translator.view.dialogs.VocabularyMenu
|
||||||
import eu.gaudian.translator.viewmodel.CategoryViewModel
|
|
||||||
import eu.gaudian.translator.viewmodel.ExerciseViewModel
|
import eu.gaudian.translator.viewmodel.ExerciseViewModel
|
||||||
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
@@ -94,9 +92,8 @@ fun MainVocabularyScreen(
|
|||||||
) {
|
) {
|
||||||
|
|
||||||
val activity = LocalActivity.current as ComponentActivity
|
val activity = LocalActivity.current as ComponentActivity
|
||||||
val vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(activity.application as Application)
|
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val categoryViewModel: CategoryViewModel = viewModel(viewModelStoreOwner = activity)
|
val exerciseViewModel: ExerciseViewModel = hiltViewModel(activity)
|
||||||
val exerciseViewModel: ExerciseViewModel = viewModel(viewModelStoreOwner = activity)
|
|
||||||
val vocabularyNavController = rememberNavController()
|
val vocabularyNavController = rememberNavController()
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
var showCustomExerciseDialog by remember { mutableStateOf(false) }
|
var showCustomExerciseDialog by remember { mutableStateOf(false) }
|
||||||
@@ -117,8 +114,6 @@ fun MainVocabularyScreen(
|
|||||||
|
|
||||||
if (showCustomExerciseDialog) {
|
if (showCustomExerciseDialog) {
|
||||||
StartExerciseDialog(
|
StartExerciseDialog(
|
||||||
vocabularyViewModel = vocabularyViewModel,
|
|
||||||
categoryViewModel = categoryViewModel,
|
|
||||||
onDismiss = { showCustomExerciseDialog = false },
|
onDismiss = { showCustomExerciseDialog = false },
|
||||||
onConfirm = { categories, stages, languageIds ->
|
onConfirm = { categories, stages, languageIds ->
|
||||||
showCustomExerciseDialog = false
|
showCustomExerciseDialog = false
|
||||||
@@ -133,8 +128,6 @@ fun MainVocabularyScreen(
|
|||||||
|
|
||||||
if (showWordPairExerciseDialog) {
|
if (showWordPairExerciseDialog) {
|
||||||
StartExerciseDialog(
|
StartExerciseDialog(
|
||||||
vocabularyViewModel = vocabularyViewModel,
|
|
||||||
categoryViewModel = categoryViewModel,
|
|
||||||
onDismiss = { showWordPairExerciseDialog = false },
|
onDismiss = { showWordPairExerciseDialog = false },
|
||||||
onConfirm = { categories, stages, languageIds ->
|
onConfirm = { categories, stages, languageIds ->
|
||||||
// Store selections and open settings dialog instead of starting immediately
|
// Store selections and open settings dialog instead of starting immediately
|
||||||
|
|||||||
@@ -30,11 +30,14 @@ import androidx.compose.runtime.rememberCoroutineScope
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
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.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
|
||||||
import eu.gaudian.translator.view.composable.AppSlider
|
import eu.gaudian.translator.view.composable.AppSlider
|
||||||
@@ -45,14 +48,16 @@ import kotlinx.coroutines.launch
|
|||||||
@Composable
|
@Composable
|
||||||
fun NoGrammarItemsScreen(
|
fun NoGrammarItemsScreen(
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(androidx.compose.ui.platform.LocalContext.current.applicationContext as android.app.Application)
|
|
||||||
) {
|
) {
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
|
||||||
val itemsWithoutGrammar by vocabularyViewModel.allItemsWithoutGrammar.collectAsState()
|
val itemsWithoutGrammar by vocabularyViewModel.allItemsWithoutGrammar.collectAsState()
|
||||||
val isGenerating by vocabularyViewModel.isGenerating.collectAsState()
|
val isGenerating by vocabularyViewModel.isGenerating.collectAsState()
|
||||||
|
|
||||||
var showFetchGrammarDialog by remember { mutableStateOf(false) }
|
var showFetchGrammarDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
@Suppress("UnusedVariable") val onClose = { navController.popBackStack() }
|
@Suppress("UnusedVariable", "unused", "HardCodedStringLiteral") val onClose = { navController.popBackStack() }
|
||||||
|
|
||||||
if (itemsWithoutGrammar.isEmpty() && !isGenerating) {
|
if (itemsWithoutGrammar.isEmpty() && !isGenerating) {
|
||||||
Column(
|
Column(
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
package eu.gaudian.translator.view.vocabulary
|
package eu.gaudian.translator.view.vocabulary
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -32,20 +31,16 @@ import eu.gaudian.translator.view.LocalConnectionConfigured
|
|||||||
import eu.gaudian.translator.view.composable.AppButton
|
import eu.gaudian.translator.view.composable.AppButton
|
||||||
import eu.gaudian.translator.view.dialogs.AddVocabularyDialog
|
import eu.gaudian.translator.view.dialogs.AddVocabularyDialog
|
||||||
import eu.gaudian.translator.view.dialogs.ImportVocabularyDialog
|
import eu.gaudian.translator.view.dialogs.ImportVocabularyDialog
|
||||||
import eu.gaudian.translator.viewmodel.CategoryViewModel
|
|
||||||
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
||||||
import eu.gaudian.translator.viewmodel.StatusViewModel
|
|
||||||
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NoVocabularyScreen(){
|
fun NoVocabularyScreen(){
|
||||||
|
|
||||||
val application = LocalContext.current.applicationContext as Application
|
|
||||||
val statusViewModel = StatusViewModel.getInstance(application)
|
|
||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
|
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val vocabularyViewModel = VocabularyViewModel.getInstance(application)
|
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val categoryViewModel = CategoryViewModel.getInstance(application)
|
|
||||||
|
|
||||||
|
|
||||||
var showAddVocabularyDialog by remember { mutableStateOf(false) }
|
var showAddVocabularyDialog by remember { mutableStateOf(false) }
|
||||||
@@ -56,9 +51,6 @@ fun NoVocabularyScreen(){
|
|||||||
|
|
||||||
if (showAddVocabularyDialog) {
|
if (showAddVocabularyDialog) {
|
||||||
AddVocabularyDialog(
|
AddVocabularyDialog(
|
||||||
statusViewModel = statusViewModel,
|
|
||||||
languageViewModel = languageViewModel,
|
|
||||||
vocabularyViewModel = vocabularyViewModel,
|
|
||||||
onDismissRequest = { showAddVocabularyDialog = false }
|
onDismissRequest = { showAddVocabularyDialog = false }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -66,7 +58,6 @@ fun NoVocabularyScreen(){
|
|||||||
if (showImportVocabularyDialog) {
|
if (showImportVocabularyDialog) {
|
||||||
ImportVocabularyDialog(
|
ImportVocabularyDialog(
|
||||||
languageViewModel = languageViewModel,
|
languageViewModel = languageViewModel,
|
||||||
categoryViewModel = categoryViewModel,
|
|
||||||
vocabularyViewModel = vocabularyViewModel,
|
vocabularyViewModel = vocabularyViewModel,
|
||||||
onDismiss = { showImportVocabularyDialog = false }
|
onDismiss = { showImportVocabularyDialog = false }
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package eu.gaudian.translator.view.vocabulary
|
package eu.gaudian.translator.view.vocabulary
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
@@ -10,24 +9,28 @@ import androidx.compose.runtime.Composable
|
|||||||
import androidx.compose.runtime.collectAsState
|
import androidx.compose.runtime.collectAsState
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.model.VocabularyStage
|
import eu.gaudian.translator.model.VocabularyStage
|
||||||
|
import eu.gaudian.translator.utils.findActivity
|
||||||
import eu.gaudian.translator.view.composable.AppIcons
|
import eu.gaudian.translator.view.composable.AppIcons
|
||||||
import eu.gaudian.translator.view.composable.AppScaffold
|
import eu.gaudian.translator.view.composable.AppScaffold
|
||||||
import eu.gaudian.translator.view.composable.AppTopAppBar
|
import eu.gaudian.translator.view.composable.AppTopAppBar
|
||||||
import eu.gaudian.translator.view.vocabulary.widgets.DetailedStageProgressBar
|
import eu.gaudian.translator.view.vocabulary.widgets.DetailedStageProgressBar
|
||||||
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
||||||
import okhttp3.internal.platform.PlatformRegistry.applicationContext
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun StageDetailScreen(
|
fun StageDetailScreen(
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
stage: VocabularyStage,
|
stage: VocabularyStage,
|
||||||
viewModel: VocabularyViewModel = VocabularyViewModel.getInstance(applicationContext as Application),
|
|
||||||
) {
|
) {
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val viewModel = hiltViewModel<VocabularyViewModel>(viewModelStoreOwner = activity)
|
||||||
|
|
||||||
val stageMapping by viewModel.stageMapping.collectAsState()
|
val stageMapping by viewModel.stageMapping.collectAsState()
|
||||||
val dueTodayItems by viewModel.dueTodayItems.collectAsState()
|
val dueTodayItems by viewModel.dueTodayItems.collectAsState()
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package eu.gaudian.translator.view.vocabulary
|
package eu.gaudian.translator.view.vocabulary
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import androidx.compose.animation.animateColorAsState
|
import androidx.compose.animation.animateColorAsState
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.foundation.BorderStroke
|
import androidx.compose.foundation.BorderStroke
|
||||||
@@ -79,9 +78,9 @@ fun StartScreen(
|
|||||||
selectedTargetLanguage: Language?,
|
selectedTargetLanguage: Language?,
|
||||||
onTargetLanguageChanged: (Language?) -> Unit,
|
onTargetLanguageChanged: (Language?) -> Unit,
|
||||||
) {
|
) {
|
||||||
val vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(
|
|
||||||
LocalContext.current.applicationContext as Application
|
val activity = LocalContext.current.findActivity()
|
||||||
)
|
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val dueTodayItems by vocabularyViewModel.dueTodayItems.collectAsState(initial = emptyList())
|
val dueTodayItems by vocabularyViewModel.dueTodayItems.collectAsState(initial = emptyList())
|
||||||
val allItems = cardSet?.cards ?: emptyList()
|
val allItems = cardSet?.cards ?: emptyList()
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package eu.gaudian.translator.view.vocabulary
|
package eu.gaudian.translator.view.vocabulary
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
@@ -28,7 +27,6 @@ import androidx.compose.ui.unit.dp
|
|||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.model.VocabularyItem
|
import eu.gaudian.translator.model.VocabularyItem
|
||||||
@@ -44,7 +42,6 @@ import eu.gaudian.translator.view.dialogs.CategorySelectionDialog
|
|||||||
import eu.gaudian.translator.view.dialogs.ImportVocabularyDialog
|
import eu.gaudian.translator.view.dialogs.ImportVocabularyDialog
|
||||||
import eu.gaudian.translator.view.dialogs.StageSelectionDialog
|
import eu.gaudian.translator.view.dialogs.StageSelectionDialog
|
||||||
import eu.gaudian.translator.view.vocabulary.card.VocabularyCard
|
import eu.gaudian.translator.view.vocabulary.card.VocabularyCard
|
||||||
import eu.gaudian.translator.viewmodel.CategoryViewModel
|
|
||||||
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
||||||
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -61,9 +58,8 @@ fun VocabularyCardHost(
|
|||||||
onBackPressed: (() -> Unit)? = null
|
onBackPressed: (() -> Unit)? = null
|
||||||
) {
|
) {
|
||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
val vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(LocalContext.current.applicationContext as Application)
|
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val categoryViewModel: CategoryViewModel = viewModel()
|
|
||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
|
|
||||||
val navigationItems by vocabularyViewModel.currentNavigationItems.collectAsState()
|
val navigationItems by vocabularyViewModel.currentNavigationItems.collectAsState()
|
||||||
@@ -161,6 +157,7 @@ fun VocabularyCardHost(
|
|||||||
lifecycle = lifecycleOwner.lifecycle
|
lifecycle = lifecycleOwner.lifecycle
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Suppress("ControlFlowWithEmptyBody")
|
||||||
if (exerciseMode && onBackPressed != null) {
|
if (exerciseMode && onBackPressed != null) {
|
||||||
//TODO consider BackPressHandler(onBackPressed = { showQuitDialog = true })
|
//TODO consider BackPressHandler(onBackPressed = { showQuitDialog = true })
|
||||||
}
|
}
|
||||||
@@ -213,7 +210,6 @@ fun VocabularyCardHost(
|
|||||||
onDeleteClick = { showDeleteDialog = true },
|
onDeleteClick = { showDeleteDialog = true },
|
||||||
navController = navController,
|
navController = navController,
|
||||||
isUserSpellingCorrect = false,
|
isUserSpellingCorrect = false,
|
||||||
vocabularyViewModel = vocabularyViewModel
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// Dialogs are unaffected by the layout change
|
// Dialogs are unaffected by the layout change
|
||||||
@@ -237,7 +233,6 @@ fun VocabularyCardHost(
|
|||||||
|
|
||||||
if (showCategoryDialog) {
|
if (showCategoryDialog) {
|
||||||
CategorySelectionDialog(
|
CategorySelectionDialog(
|
||||||
viewModel = categoryViewModel,
|
|
||||||
onCategorySelected = {
|
onCategorySelected = {
|
||||||
vocabularyViewModel.addVocabularyItemToCategories(
|
vocabularyViewModel.addVocabularyItemToCategories(
|
||||||
listOf(currentVocabularyItem),
|
listOf(currentVocabularyItem),
|
||||||
@@ -270,12 +265,12 @@ fun VocabularyCardHost(
|
|||||||
languageViewModel = languageViewModel,
|
languageViewModel = languageViewModel,
|
||||||
optionalDescription = stringResource(R.string.generate_related_vocabulary_items),
|
optionalDescription = stringResource(R.string.generate_related_vocabulary_items),
|
||||||
optionalSearchTerm = currentVocabularyItem.wordFirst,
|
optionalSearchTerm = currentVocabularyItem.wordFirst,
|
||||||
categoryViewModel = categoryViewModel,
|
|
||||||
vocabularyViewModel = vocabularyViewModel
|
vocabularyViewModel = vocabularyViewModel
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(spellingMode) {
|
LaunchedEffect(spellingMode) {
|
||||||
|
@Suppress("ControlFlowWithEmptyBody")
|
||||||
if (spellingMode) {
|
if (spellingMode) {
|
||||||
//TODO implement // Setup spelling mode logic if needed
|
//TODO implement // Setup spelling mode logic if needed
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,7 +50,6 @@ import eu.gaudian.translator.view.composable.AppButton
|
|||||||
import eu.gaudian.translator.view.composable.ComponentDefaults
|
import eu.gaudian.translator.view.composable.ComponentDefaults
|
||||||
import eu.gaudian.translator.view.vocabulary.card.VocabularyCard
|
import eu.gaudian.translator.view.vocabulary.card.VocabularyCard
|
||||||
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
import eu.gaudian.translator.viewmodel.LanguageViewModel
|
||||||
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Represents the different types of exercises available.
|
* Represents the different types of exercises available.
|
||||||
@@ -140,15 +139,14 @@ sealed interface VocabularyExerciseAction {
|
|||||||
fun GuessingExercise(
|
fun GuessingExercise(
|
||||||
state: VocabularyExerciseState.Guessing,
|
state: VocabularyExerciseState.Guessing,
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(LocalContext.current.applicationContext as android.app.Application)
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
VocabularyCard(
|
VocabularyCard(
|
||||||
vocabularyItem = state.item,
|
vocabularyItem = state.item,
|
||||||
isFlipped = state.isRevealed,
|
isFlipped = state.isRevealed,
|
||||||
navController = navController,
|
navController = navController,
|
||||||
exerciseMode = true,
|
exerciseMode = true,
|
||||||
switchOrder = state.isSwitched,
|
switchOrder = state.isSwitched,
|
||||||
vocabularyViewModel = vocabularyViewModel
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,8 +156,8 @@ fun GuessingExercise(
|
|||||||
fun SpellingExercise(
|
fun SpellingExercise(
|
||||||
state: VocabularyExerciseState.Spelling,
|
state: VocabularyExerciseState.Spelling,
|
||||||
navController: NavController,
|
navController: NavController,
|
||||||
vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(LocalContext.current.applicationContext as android.app.Application)
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
VocabularyCard(
|
VocabularyCard(
|
||||||
vocabularyItem = state.item,
|
vocabularyItem = state.item,
|
||||||
isFlipped = state.isRevealed,
|
isFlipped = state.isRevealed,
|
||||||
@@ -168,7 +166,6 @@ fun SpellingExercise(
|
|||||||
navController = navController,
|
navController = navController,
|
||||||
exerciseMode = true,
|
exerciseMode = true,
|
||||||
switchOrder = state.isSwitched,
|
switchOrder = state.isSwitched,
|
||||||
vocabularyViewModel = vocabularyViewModel
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,11 +23,13 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
|||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
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.model.Language
|
import eu.gaudian.translator.model.Language
|
||||||
|
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.ExerciseConfig
|
import eu.gaudian.translator.viewmodel.ExerciseConfig
|
||||||
import eu.gaudian.translator.viewmodel.ScreenState
|
import eu.gaudian.translator.viewmodel.ScreenState
|
||||||
@@ -49,8 +51,9 @@ fun VocabularyExerciseHostScreen(
|
|||||||
onClose: () -> Unit,
|
onClose: () -> Unit,
|
||||||
navController: NavController
|
navController: NavController
|
||||||
) {
|
) {
|
||||||
val vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(androidx.compose.ui.platform.LocalContext.current.applicationContext as android.app.Application)
|
val activity = LocalContext.current.findActivity()
|
||||||
val exerciseViewModel: VocabularyExerciseViewModel = viewModel()
|
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
val exerciseViewModel: VocabularyExerciseViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
|
||||||
val cardSet by vocabularyViewModel.cardSet.collectAsState()
|
val cardSet by vocabularyViewModel.cardSet.collectAsState()
|
||||||
val screenState by exerciseViewModel.screenState.collectAsState()
|
val screenState by exerciseViewModel.screenState.collectAsState()
|
||||||
|
|||||||
@@ -42,14 +42,17 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
|
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.findActivity
|
||||||
import eu.gaudian.translator.view.composable.AppIcons
|
import eu.gaudian.translator.view.composable.AppIcons
|
||||||
import eu.gaudian.translator.view.composable.AppScaffold
|
import eu.gaudian.translator.view.composable.AppScaffold
|
||||||
import eu.gaudian.translator.view.composable.AppTopAppBar
|
import eu.gaudian.translator.view.composable.AppTopAppBar
|
||||||
@@ -75,8 +78,11 @@ private data class HeatmapMonth(val yearMonth: YearMonth, val weeks: List<List<L
|
|||||||
@OptIn(ExperimentalTime::class)
|
@OptIn(ExperimentalTime::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun VocabularyHeatmapScreen(
|
fun VocabularyHeatmapScreen(
|
||||||
viewModel: ProgressViewModel = ProgressViewModel.getInstance(androidx.compose.ui.platform.LocalContext.current.applicationContext as android.app.Application), navController: NavController
|
navController: NavController
|
||||||
) {
|
) {
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val viewModel : ProgressViewModel = hiltViewModel(activity)
|
||||||
|
|
||||||
// State and derived data
|
// State and derived data
|
||||||
val dailyStats by viewModel.dailyVocabularyStats.collectAsState()
|
val dailyStats by viewModel.dailyVocabularyStats.collectAsState()
|
||||||
val today = remember { Clock.System.todayIn(TimeZone.currentSystemDefault()) }
|
val today = remember { Clock.System.todayIn(TimeZone.currentSystemDefault()) }
|
||||||
|
|||||||
@@ -71,7 +71,6 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import androidx.navigation.compose.rememberNavController
|
import androidx.navigation.compose.rememberNavController
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
@@ -126,9 +125,9 @@ fun VocabularyListScreen(
|
|||||||
val scope = rememberCoroutineScope()
|
val scope = rememberCoroutineScope()
|
||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
val lazyListState = rememberLazyListState()
|
val lazyListState = rememberLazyListState()
|
||||||
val vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(LocalContext.current.applicationContext as android.app.Application)
|
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val categoryViewModel: CategoryViewModel = viewModel()
|
val categoryViewModel: CategoryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
|
|
||||||
var filterState by rememberSaveable {
|
var filterState by rememberSaveable {
|
||||||
@@ -380,7 +379,6 @@ fun VocabularyListScreen(
|
|||||||
showFilterSheet = false
|
showFilterSheet = false
|
||||||
scope.launch { lazyListState.scrollToItem(0) }
|
scope.launch { lazyListState.scrollToItem(0) }
|
||||||
},
|
},
|
||||||
categoryViewModel = categoryViewModel,
|
|
||||||
languageViewModel = languageViewModel,
|
languageViewModel = languageViewModel,
|
||||||
languagesPresent = allLanguages.filter { it.nameResId in languagesPresent },
|
languagesPresent = allLanguages.filter { it.nameResId in languagesPresent },
|
||||||
hideCategory = categoryId != null && categoryId != 0,
|
hideCategory = categoryId != null && categoryId != 0,
|
||||||
@@ -391,7 +389,6 @@ fun VocabularyListScreen(
|
|||||||
if (showCategoryDialog) {
|
if (showCategoryDialog) {
|
||||||
val selectedItems = vocabularyItems.filter { selection.contains(it.id.toLong()) }
|
val selectedItems = vocabularyItems.filter { selection.contains(it.id.toLong()) }
|
||||||
CategorySelectionDialog(
|
CategorySelectionDialog(
|
||||||
viewModel = categoryViewModel,
|
|
||||||
onCategorySelected = {
|
onCategorySelected = {
|
||||||
vocabularyViewModel.addVocabularyItemToCategories(
|
vocabularyViewModel.addVocabularyItemToCategories(
|
||||||
selectedItems,
|
selectedItems,
|
||||||
@@ -805,7 +802,6 @@ fun LanguageRowPreview() {
|
|||||||
@Composable
|
@Composable
|
||||||
private fun FilterSortBottomSheet(
|
private fun FilterSortBottomSheet(
|
||||||
currentFilterState: VocabularyFilterState,
|
currentFilterState: VocabularyFilterState,
|
||||||
categoryViewModel: CategoryViewModel,
|
|
||||||
languageViewModel: LanguageViewModel,
|
languageViewModel: LanguageViewModel,
|
||||||
languagesPresent: List<Language>,
|
languagesPresent: List<Language>,
|
||||||
onDismiss: () -> Unit,
|
onDismiss: () -> Unit,
|
||||||
@@ -821,8 +817,9 @@ private fun FilterSortBottomSheet(
|
|||||||
|
|
||||||
val context = LocalContext.current
|
val context = LocalContext.current
|
||||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
|
||||||
val languageConfigViewModel: LanguageConfigViewModel = viewModel()
|
val languageConfigViewModel: LanguageConfigViewModel = hiltViewModel(activity)
|
||||||
val allWordClasses by languageConfigViewModel.allWordClasses.collectAsStateWithLifecycle()
|
val allWordClasses by languageConfigViewModel.allWordClasses.collectAsStateWithLifecycle()
|
||||||
|
|
||||||
ModalBottomSheet(
|
ModalBottomSheet(
|
||||||
@@ -896,7 +893,6 @@ private fun FilterSortBottomSheet(
|
|||||||
Text(stringResource(R.string.label_category), style = MaterialTheme.typography.titleMedium)
|
Text(stringResource(R.string.label_category), style = MaterialTheme.typography.titleMedium)
|
||||||
Spacer(Modifier.height(8.dp))
|
Spacer(Modifier.height(8.dp))
|
||||||
CategoryDropdown(
|
CategoryDropdown(
|
||||||
categoryViewModel = categoryViewModel,
|
|
||||||
initialCategoryId = selectedCategoryId,
|
initialCategoryId = selectedCategoryId,
|
||||||
onCategorySelected = { categories ->
|
onCategorySelected = { categories ->
|
||||||
selectedCategoryId = categories.firstOrNull()?.id
|
selectedCategoryId = categories.firstOrNull()?.id
|
||||||
@@ -958,11 +954,9 @@ private fun FilterSortBottomSheet(
|
|||||||
@Composable
|
@Composable
|
||||||
fun FilterSortBottomSheetPreview() {
|
fun FilterSortBottomSheetPreview() {
|
||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
val categoryViewModel: CategoryViewModel = viewModel()
|
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
FilterSortBottomSheet(
|
FilterSortBottomSheet(
|
||||||
currentFilterState = VocabularyFilterState(),
|
currentFilterState = VocabularyFilterState(),
|
||||||
categoryViewModel = categoryViewModel,
|
|
||||||
languageViewModel = languageViewModel,
|
languageViewModel = languageViewModel,
|
||||||
languagesPresent = emptyList(),
|
languagesPresent = emptyList(),
|
||||||
onDismiss = {},
|
onDismiss = {},
|
||||||
|
|||||||
@@ -56,7 +56,6 @@ import androidx.compose.ui.unit.Dp
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.compose.ui.unit.sp
|
import androidx.compose.ui.unit.sp
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import androidx.navigation.NavHostController
|
import androidx.navigation.NavHostController
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.model.Language
|
import eu.gaudian.translator.model.Language
|
||||||
@@ -82,7 +81,6 @@ import eu.gaudian.translator.viewmodel.LanguageViewModel
|
|||||||
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import okhttp3.internal.platform.PlatformRegistry.applicationContext
|
|
||||||
|
|
||||||
enum class FilterMode {
|
enum class FilterMode {
|
||||||
NEW, DUPLICATES, FAULTY
|
NEW, DUPLICATES, FAULTY
|
||||||
@@ -90,11 +88,14 @@ enum class FilterMode {
|
|||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun VocabularySortingScreen(
|
fun VocabularySortingScreen(
|
||||||
vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(applicationContext as android.app.Application),
|
|
||||||
categoryViewModel: CategoryViewModel = CategoryViewModel.getInstance(applicationContext as android.app.Application),
|
|
||||||
navController: NavHostController,
|
navController: NavHostController,
|
||||||
initialFilterMode: String? = null
|
initialFilterMode: String? = null
|
||||||
) {
|
) {
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
val categoryViewModel: CategoryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
|
||||||
|
|
||||||
var sortOrder by remember { mutableStateOf(VocabularyViewModel.SortOrder.NEWEST_FIRST) }
|
var sortOrder by remember { mutableStateOf(VocabularyViewModel.SortOrder.NEWEST_FIRST) }
|
||||||
val allDuplicateItems by vocabularyViewModel.allDuplicateItems.collectAsState()
|
val allDuplicateItems by vocabularyViewModel.allDuplicateItems.collectAsState()
|
||||||
val allFaultyItems by vocabularyViewModel.allFaultyItems.collectAsState()
|
val allFaultyItems by vocabularyViewModel.allFaultyItems.collectAsState()
|
||||||
@@ -263,9 +264,7 @@ fun VocabularySortingScreen(
|
|||||||
VocabularySortingItem(
|
VocabularySortingItem(
|
||||||
item = item,
|
item = item,
|
||||||
filterMode = filterMode, // NEW: Pass the mode to the item
|
filterMode = filterMode, // NEW: Pass the mode to the item
|
||||||
vocabularyViewModel = vocabularyViewModel,
|
vocabularyViewModel = vocabularyViewModel
|
||||||
categoryViewModel = categoryViewModel,
|
|
||||||
languageConfigViewModel = viewModel()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -286,8 +285,6 @@ fun VocabularySortingScreen(
|
|||||||
@Composable
|
@Composable
|
||||||
fun VocabularySortingScreenPreview() {
|
fun VocabularySortingScreenPreview() {
|
||||||
VocabularySortingScreen(
|
VocabularySortingScreen(
|
||||||
vocabularyViewModel = viewModel(),
|
|
||||||
categoryViewModel = viewModel(),
|
|
||||||
navController = NavHostController(LocalContext.current),
|
navController = NavHostController(LocalContext.current),
|
||||||
initialFilterMode = "NEW"
|
initialFilterMode = "NEW"
|
||||||
)
|
)
|
||||||
@@ -296,12 +293,11 @@ fun VocabularySortingScreenPreview() {
|
|||||||
@Composable
|
@Composable
|
||||||
fun VocabularySortingItem(
|
fun VocabularySortingItem(
|
||||||
item: VocabularyItem,
|
item: VocabularyItem,
|
||||||
filterMode: FilterMode?, // NEW: Receive the current filter mode
|
filterMode: FilterMode?,
|
||||||
vocabularyViewModel: VocabularyViewModel,
|
vocabularyViewModel: VocabularyViewModel
|
||||||
categoryViewModel: CategoryViewModel,
|
|
||||||
languageConfigViewModel: LanguageConfigViewModel = viewModel()
|
|
||||||
) {
|
) {
|
||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val languageConfigViewModel: LanguageConfigViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
var wordFirst by remember { mutableStateOf(item.wordFirst) }
|
var wordFirst by remember { mutableStateOf(item.wordFirst) }
|
||||||
var wordSecond by remember { mutableStateOf(item.wordSecond) }
|
var wordSecond by remember { mutableStateOf(item.wordSecond) }
|
||||||
@@ -487,7 +483,6 @@ fun VocabularySortingItem(
|
|||||||
}
|
}
|
||||||
|
|
||||||
CategoryDropdown(
|
CategoryDropdown(
|
||||||
categoryViewModel = categoryViewModel,
|
|
||||||
onCategorySelected = { categories ->
|
onCategorySelected = { categories ->
|
||||||
selectedCategories = categories.mapNotNull { it?.id }
|
selectedCategories = categories.mapNotNull { it?.id }
|
||||||
},
|
},
|
||||||
@@ -560,9 +555,7 @@ fun VocabularySortingItemPreview() {
|
|||||||
VocabularySortingItem(
|
VocabularySortingItem(
|
||||||
item = item,
|
item = item,
|
||||||
filterMode = FilterMode.NEW,
|
filterMode = FilterMode.NEW,
|
||||||
vocabularyViewModel = viewModel(),
|
vocabularyViewModel = hiltViewModel()
|
||||||
categoryViewModel = viewModel(),
|
|
||||||
languageConfigViewModel = viewModel()
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ fun AdditionalContentBottomSheet(
|
|||||||
) {
|
) {
|
||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
|
|
||||||
val vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(applicationContext as Application)
|
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val dictionaryViewModel: DictionaryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val dictionaryViewModel: DictionaryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val coroutineScope = rememberCoroutineScope()
|
val coroutineScope = rememberCoroutineScope()
|
||||||
@@ -225,7 +225,6 @@ fun AdditionalContentBottomSheet(
|
|||||||
SynonymsDisplay(
|
SynonymsDisplay(
|
||||||
synonyms = synonyms,
|
synonyms = synonyms,
|
||||||
loading = synonymsLoading,
|
loading = synonymsLoading,
|
||||||
vocabularyViewModel = vocabularyViewModel,
|
|
||||||
onSynonymAdded = handleSynonymAdded,
|
onSynonymAdded = handleSynonymAdded,
|
||||||
onReload = refreshSynonyms
|
onReload = refreshSynonyms
|
||||||
)
|
)
|
||||||
@@ -320,10 +319,12 @@ private fun ExampleSentencesDisplayLoadedPreview() {
|
|||||||
private fun SynonymsDisplay(
|
private fun SynonymsDisplay(
|
||||||
synonyms: List<SynonymDisplayState>?,
|
synonyms: List<SynonymDisplayState>?,
|
||||||
loading: Boolean,
|
loading: Boolean,
|
||||||
vocabularyViewModel: VocabularyViewModel,
|
|
||||||
onSynonymAdded: () -> Unit,
|
onSynonymAdded: () -> Unit,
|
||||||
onReload: () -> Unit,
|
onReload: () -> Unit,
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
Column {
|
Column {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
@@ -387,7 +388,7 @@ private fun SynonymsDisplay(
|
|||||||
@Composable
|
@Composable
|
||||||
private fun SynonymsDisplayLoadingPreview() {
|
private fun SynonymsDisplayLoadingPreview() {
|
||||||
// This preview doesn't have a real VocabularyViewModel, so actions will not work.
|
// This preview doesn't have a real VocabularyViewModel, so actions will not work.
|
||||||
SynonymsDisplay(synonyms = null, loading = false, vocabularyViewModel = VocabularyViewModel.getInstance(applicationContext as Application), onSynonymAdded = {}, onReload = {})
|
SynonymsDisplay(synonyms = null, loading = false, onSynonymAdded = {}, onReload = {})
|
||||||
}
|
}
|
||||||
|
|
||||||
@Suppress("HardCodedStringLiteral")
|
@Suppress("HardCodedStringLiteral")
|
||||||
@@ -415,7 +416,7 @@ private fun SynonymsDisplayLoadedPreview() {
|
|||||||
proximity = null
|
proximity = null
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
SynonymsDisplay(synonyms = synonyms, loading = false, vocabularyViewModel = VocabularyViewModel.getInstance(applicationContext as Application), onSynonymAdded = {}, onReload = {})
|
SynonymsDisplay(synonyms = synonyms, loading = false, onSynonymAdded = {}, onReload = {})
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -29,11 +29,12 @@ import androidx.compose.runtime.setValue
|
|||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.blur
|
import androidx.compose.ui.draw.blur
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontStyle
|
import androidx.compose.ui.text.font.FontStyle
|
||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.model.Language
|
import eu.gaudian.translator.model.Language
|
||||||
import eu.gaudian.translator.model.grammar.CategoryConfig
|
import eu.gaudian.translator.model.grammar.CategoryConfig
|
||||||
@@ -42,6 +43,7 @@ import eu.gaudian.translator.model.grammar.GrammaticalFeature
|
|||||||
import eu.gaudian.translator.model.grammar.LanguageConfig
|
import eu.gaudian.translator.model.grammar.LanguageConfig
|
||||||
import eu.gaudian.translator.model.grammar.formatGrammarDetails
|
import eu.gaudian.translator.model.grammar.formatGrammarDetails
|
||||||
import eu.gaudian.translator.utils.Log
|
import eu.gaudian.translator.utils.Log
|
||||||
|
import eu.gaudian.translator.utils.findActivity
|
||||||
import eu.gaudian.translator.view.composable.AppDialog
|
import eu.gaudian.translator.view.composable.AppDialog
|
||||||
import eu.gaudian.translator.view.composable.AppTextField
|
import eu.gaudian.translator.view.composable.AppTextField
|
||||||
import eu.gaudian.translator.view.composable.DialogButton
|
import eu.gaudian.translator.view.composable.DialogButton
|
||||||
@@ -54,8 +56,9 @@ internal fun GrammarDetailsText(
|
|||||||
isRevealed: Boolean
|
isRevealed: Boolean
|
||||||
) {
|
) {
|
||||||
Log.d("GrammarComponents", "GrammarDetailsText called with details: $details, language: $language, isRevealed: $isRevealed")
|
Log.d("GrammarComponents", "GrammarDetailsText called with details: $details, language: $language, isRevealed: $isRevealed")
|
||||||
|
|
||||||
val configViewModel: LanguageConfigViewModel = viewModel()
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val configViewModel: LanguageConfigViewModel = hiltViewModel(activity)
|
||||||
val allConfigs by configViewModel.configs.collectAsState()
|
val allConfigs by configViewModel.configs.collectAsState()
|
||||||
val languageConfig = language?.code?.let { allConfigs[it] }
|
val languageConfig = language?.code?.let { allConfigs[it] }
|
||||||
val categoryConfig = details?.category?.let { languageConfig?.categories?.get(it) }
|
val categoryConfig = details?.category?.let { languageConfig?.categories?.get(it) }
|
||||||
@@ -88,8 +91,9 @@ internal fun GrammarEditDialog(
|
|||||||
onSave: (GrammaticalFeature?) -> Unit
|
onSave: (GrammaticalFeature?) -> Unit
|
||||||
) {
|
) {
|
||||||
Log.d("GrammarComponents", "GrammarEditDialog called with feature: $feature, language: $language")
|
Log.d("GrammarComponents", "GrammarEditDialog called with feature: $feature, language: $language")
|
||||||
|
|
||||||
val configViewModel: LanguageConfigViewModel = viewModel()
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val configViewModel: LanguageConfigViewModel = hiltViewModel(activity)
|
||||||
val allConfigs by configViewModel.configs.collectAsState()
|
val allConfigs by configViewModel.configs.collectAsState()
|
||||||
val languageConfig = allConfigs[language.code]
|
val languageConfig = allConfigs[language.code]
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package eu.gaudian.translator.view.vocabulary.card
|
package eu.gaudian.translator.view.vocabulary.card
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.foundation.BorderStroke
|
import androidx.compose.foundation.BorderStroke
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
@@ -54,7 +53,6 @@ import androidx.compose.ui.text.withStyle
|
|||||||
import androidx.compose.ui.tooling.preview.Preview
|
import androidx.compose.ui.tooling.preview.Preview
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.model.Language
|
import eu.gaudian.translator.model.Language
|
||||||
@@ -79,7 +77,6 @@ import eu.gaudian.translator.viewmodel.MessageDisplayType
|
|||||||
import eu.gaudian.translator.viewmodel.SettingsViewModel
|
import eu.gaudian.translator.viewmodel.SettingsViewModel
|
||||||
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import okhttp3.internal.platform.PlatformRegistry.applicationContext
|
|
||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -95,12 +92,10 @@ fun VocabularyCard(
|
|||||||
onDeleteClick: () -> Unit = {},
|
onDeleteClick: () -> Unit = {},
|
||||||
userSpellingAnswer: String? = null,
|
userSpellingAnswer: String? = null,
|
||||||
isUserSpellingCorrect: Boolean? = null,
|
isUserSpellingCorrect: Boolean? = null,
|
||||||
vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(applicationContext as Application),
|
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val dictionaryViewModel: DictionaryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val dictionaryViewModel: DictionaryViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
|
|
||||||
@@ -463,7 +458,8 @@ private fun GrammarPill(
|
|||||||
isRevealed: Boolean,
|
isRevealed: Boolean,
|
||||||
onEditGrammarClick: () -> Unit
|
onEditGrammarClick: () -> Unit
|
||||||
) {
|
) {
|
||||||
val configViewModel: LanguageConfigViewModel = viewModel()
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val configViewModel: LanguageConfigViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val allConfigs by configViewModel.configs.collectAsState()
|
val allConfigs by configViewModel.configs.collectAsState()
|
||||||
val languageConfig = language?.code?.let { allConfigs[it] }
|
val languageConfig = language?.code?.let { allConfigs[it] }
|
||||||
val categoryConfig = wordDetails?.category?.let { languageConfig?.categories?.get(it) }
|
val categoryConfig = wordDetails?.category?.let { languageConfig?.categories?.get(it) }
|
||||||
|
|||||||
@@ -35,14 +35,17 @@ import androidx.compose.ui.Modifier
|
|||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.StrokeCap
|
import androidx.compose.ui.graphics.StrokeCap
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
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.ui.theme.ThemePreviews
|
import eu.gaudian.translator.ui.theme.ThemePreviews
|
||||||
import eu.gaudian.translator.ui.theme.semanticColors
|
import eu.gaudian.translator.ui.theme.semanticColors
|
||||||
|
import eu.gaudian.translator.utils.findActivity
|
||||||
import eu.gaudian.translator.view.composable.AppIcons
|
import eu.gaudian.translator.view.composable.AppIcons
|
||||||
import eu.gaudian.translator.view.composable.SecondaryButton
|
import eu.gaudian.translator.view.composable.SecondaryButton
|
||||||
import eu.gaudian.translator.viewmodel.ProgressViewModel
|
import eu.gaudian.translator.viewmodel.ProgressViewModel
|
||||||
@@ -52,7 +55,9 @@ fun CategoryProgressWidget(
|
|||||||
onCategoryClicked: (VocabularyCategory?) -> Unit,
|
onCategoryClicked: (VocabularyCategory?) -> Unit,
|
||||||
onViewAllClicked: () -> Unit
|
onViewAllClicked: () -> Unit
|
||||||
) {
|
) {
|
||||||
val viewModel: ProgressViewModel = ProgressViewModel.getInstance(androidx.compose.ui.platform.LocalContext.current.applicationContext as android.app.Application)
|
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val viewModel : ProgressViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val categoryProgressList by viewModel.categoryProgressList.collectAsState(initial = emptyList())
|
val categoryProgressList by viewModel.categoryProgressList.collectAsState(initial = emptyList())
|
||||||
val selectedCategories by viewModel.selectedCategories.collectAsState(initial = emptySet())
|
val selectedCategories by viewModel.selectedCategories.collectAsState(initial = emptySet())
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package eu.gaudian.translator.view.vocabulary.widgets
|
package eu.gaudian.translator.view.vocabulary.widgets
|
||||||
|
|
||||||
import android.app.Application
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
@@ -17,24 +16,28 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.graphics.vector.ImageVector
|
import androidx.compose.ui.graphics.vector.ImageVector
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.ui.theme.semanticColors
|
import eu.gaudian.translator.ui.theme.semanticColors
|
||||||
|
import eu.gaudian.translator.utils.findActivity
|
||||||
import eu.gaudian.translator.view.composable.AppIcons
|
import eu.gaudian.translator.view.composable.AppIcons
|
||||||
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
||||||
import okhttp3.internal.platform.PlatformRegistry.applicationContext
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun StatusWidget(
|
fun StatusWidget(
|
||||||
vocabularyViewModel: VocabularyViewModel = VocabularyViewModel.getInstance(applicationContext as Application),
|
|
||||||
onNavigateToNew: () -> Unit,
|
onNavigateToNew: () -> Unit,
|
||||||
onNavigateToDuplicates: () -> Unit,
|
onNavigateToDuplicates: () -> Unit,
|
||||||
onNavigateToFaulty: () -> Unit,
|
onNavigateToFaulty: () -> Unit,
|
||||||
onNavigateToNoGrammar: () -> Unit,
|
onNavigateToNoGrammar: () -> Unit,
|
||||||
onNavigateToMissingLanguage: (Int) -> Unit
|
onNavigateToMissingLanguage: (Int) -> Unit
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
val activity = LocalContext.current.findActivity()
|
||||||
|
val vocabularyViewModel: VocabularyViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
val newItemsCount by vocabularyViewModel.newItemsCount.collectAsState()
|
val newItemsCount by vocabularyViewModel.newItemsCount.collectAsState()
|
||||||
val duplicateCount by vocabularyViewModel.duplicateItemsCount.collectAsState()
|
val duplicateCount by vocabularyViewModel.duplicateItemsCount.collectAsState()
|
||||||
val faultyItemsCount by vocabularyViewModel.faultyItemsCount.collectAsState()
|
val faultyItemsCount by vocabularyViewModel.faultyItemsCount.collectAsState()
|
||||||
|
|||||||
@@ -5,8 +5,8 @@ package eu.gaudian.translator.viewmodel
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import eu.gaudian.translator.model.VocabularyCategory
|
import eu.gaudian.translator.model.VocabularyCategory
|
||||||
//import eu.gaudian.translator.model.VocabularyDictionary
|
|
||||||
import eu.gaudian.translator.model.repository.VocabularyRepository
|
import eu.gaudian.translator.model.repository.VocabularyRepository
|
||||||
import eu.gaudian.translator.utils.Log
|
import eu.gaudian.translator.utils.Log
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
@@ -18,35 +18,13 @@ import kotlinx.coroutines.flow.map
|
|||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
@HiltViewModel
|
||||||
* TODO: Convert CategoryViewModel to HiltViewModel
|
class CategoryViewModel @Inject constructor(
|
||||||
*
|
application: Application,
|
||||||
* This ViewModel needs to be converted to use Hilt for dependency injection.
|
private val repository: VocabularyRepository
|
||||||
* Currently it manually instantiates dependencies and uses a singleton pattern.
|
) : AndroidViewModel(application) {
|
||||||
*
|
|
||||||
* Checklist:
|
|
||||||
* - [ ] Add @HiltViewModel annotation to the class
|
|
||||||
* - [ ] Change constructor to use @Inject and inject dependencies:
|
|
||||||
* - VocabularyRepository
|
|
||||||
* - [ ] Create/update RepositoryModule.kt to provide singleton instances of:
|
|
||||||
* - VocabularyRepository
|
|
||||||
* - [ ] Modify companion object getInstance() method to use Hilt's EntryPoint system
|
|
||||||
* - [ ] Create CategoryViewModelEntryPoint interface for accessing the singleton instance
|
|
||||||
* - [ ] Remove manual dependency instantiation from constructor and init block
|
|
||||||
* - [ ] Update all places where CategoryViewModel.getInstance() is called to ensure compatibility
|
|
||||||
* - [ ] Test that the ViewModel works correctly with dependency injection
|
|
||||||
*/
|
|
||||||
class CategoryViewModel(application: Application) : AndroidViewModel(application) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
@Volatile private var INSTANCE: CategoryViewModel? = null
|
|
||||||
fun getInstance(application: Application): CategoryViewModel = INSTANCE ?: synchronized(this) {
|
|
||||||
INSTANCE ?: CategoryViewModel(application).also { INSTANCE = it }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val repository: VocabularyRepository = VocabularyRepository.getInstance(application)
|
|
||||||
|
|
||||||
val categories: StateFlow<List<VocabularyCategory>> = repository.getAllCategoriesFlow()
|
val categories: StateFlow<List<VocabularyCategory>> = repository.getAllCategoriesFlow()
|
||||||
.stateIn(
|
.stateIn(
|
||||||
@@ -146,5 +124,4 @@ class CategoryViewModel(application: Application) : AndroidViewModel(application
|
|||||||
_showEditCategoryDialog.value = show
|
_showEditCategoryDialog.value = show
|
||||||
categoryToEdit = categoryId
|
categoryToEdit = categoryId
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}
|
|
||||||
|
|||||||
@@ -13,35 +13,22 @@ import androidx.compose.ui.text.style.TextDecoration
|
|||||||
import androidx.compose.ui.text.withStyle
|
import androidx.compose.ui.text.withStyle
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import eu.gaudian.translator.model.Language
|
import eu.gaudian.translator.model.Language
|
||||||
import eu.gaudian.translator.utils.CorrectionService
|
import eu.gaudian.translator.utils.CorrectionService
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
/**
|
class CorrectionViewModel @Inject constructor(
|
||||||
* TODO: Convert CorrectionViewModel to HiltViewModel
|
application: Application,
|
||||||
*
|
private val correctionService: CorrectionService
|
||||||
* This ViewModel needs to be converted to use Hilt for dependency injection.
|
) : AndroidViewModel(application) {
|
||||||
* Currently it manually instantiates dependencies in the constructor.
|
|
||||||
*
|
|
||||||
* Checklist:
|
|
||||||
* - [ ] Add @HiltViewModel annotation to the class
|
|
||||||
* - [ ] Change constructor to use @Inject and inject dependencies:
|
|
||||||
* - CorrectionService
|
|
||||||
* - [ ] Create/update RepositoryModule.kt to provide singleton instances of:
|
|
||||||
* - CorrectionService
|
|
||||||
* - [ ] Remove manual dependency instantiation from constructor
|
|
||||||
* - [ ] Update all places where CorrectionViewModel() is instantiated to use Hilt injection
|
|
||||||
* - [ ] Test that the ViewModel works correctly with dependency injection
|
|
||||||
*/
|
|
||||||
class CorrectionViewModel(application: Application) : AndroidViewModel(application) {
|
|
||||||
|
|
||||||
enum class Tone { NONE, FORMAL, CASUAL, COLLOQUIAL, POLITE, PROFESSIONAL, FRIENDLY, ACADEMIC, CREATIVE }
|
enum class Tone { NONE, FORMAL, CASUAL, COLLOQUIAL, POLITE, PROFESSIONAL, FRIENDLY, ACADEMIC, CREATIVE }
|
||||||
|
|
||||||
val correctionService = CorrectionService(application.applicationContext)
|
|
||||||
|
|
||||||
private val _textFieldValue = MutableStateFlow(TextFieldValue(""))
|
private val _textFieldValue = MutableStateFlow(TextFieldValue(""))
|
||||||
val textFieldValue = _textFieldValue.asStateFlow()
|
val textFieldValue = _textFieldValue.asStateFlow()
|
||||||
|
|
||||||
@@ -150,4 +137,4 @@ class CorrectionViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import android.annotation.SuppressLint
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
import eu.gaudian.translator.model.CategorizationQuestion
|
import eu.gaudian.translator.model.CategorizationQuestion
|
||||||
import eu.gaudian.translator.model.Exercise
|
import eu.gaudian.translator.model.Exercise
|
||||||
@@ -35,6 +36,7 @@ import kotlinx.coroutines.flow.asStateFlow
|
|||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlin.reflect.KClass
|
import kotlin.reflect.KClass
|
||||||
|
|
||||||
sealed class AnswerResult {
|
sealed class AnswerResult {
|
||||||
@@ -85,45 +87,21 @@ sealed class YouTubeExerciseState {
|
|||||||
data class Error(val message: String) : YouTubeExerciseState()
|
data class Error(val message: String) : YouTubeExerciseState()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@HiltViewModel
|
||||||
* TODO: Convert ExerciseViewModel to HiltViewModel
|
class ExerciseViewModel @Inject constructor(
|
||||||
*
|
application: Application,
|
||||||
* This ViewModel needs to be converted to use Hilt for dependency injection.
|
private val exerciseRepository: ExerciseRepository,
|
||||||
* Currently it manually instantiates dependencies and uses a singleton pattern.
|
private val exerciseService: ExerciseService,
|
||||||
*
|
private val vocabularyRepository: VocabularyRepository,
|
||||||
* Checklist:
|
private val languageRepository: LanguageRepository,
|
||||||
* - [ ] Add @HiltViewModel annotation to the class
|
private val translationService: TranslationService,
|
||||||
* - [ ] Change constructor to use @Inject and inject dependencies:
|
private val youTubeApiService: YouTubeApiService,
|
||||||
* - ExerciseRepository
|
private val youTubeExerciseService: YouTubeExerciseService
|
||||||
* - ExerciseService
|
) : AndroidViewModel(application) {
|
||||||
* - VocabularyRepository
|
|
||||||
* - LanguageRepository
|
|
||||||
* - TranslationService
|
|
||||||
* - YouTubeApiService
|
|
||||||
* - YouTubeExerciseService
|
|
||||||
* - [ ] Create/update RepositoryModule.kt to provide singleton instances of:
|
|
||||||
* - ExerciseRepository
|
|
||||||
* - ExerciseService
|
|
||||||
* - VocabularyRepository
|
|
||||||
* - LanguageRepository
|
|
||||||
* - TranslationService
|
|
||||||
* - YouTubeApiService
|
|
||||||
* - YouTubeExerciseService
|
|
||||||
* - [ ] Modify companion object getInstance() method to use Hilt's EntryPoint system
|
|
||||||
* - [ ] Create ExerciseViewModelEntryPoint interface for accessing the singleton instance
|
|
||||||
* - [ ] Remove manual dependency instantiation from constructor and init block
|
|
||||||
* - [ ] Update all places where ExerciseViewModel.getInstance() is called to ensure compatibility
|
|
||||||
* - [ ] Test that the ViewModel works correctly with dependency injection
|
|
||||||
*/
|
|
||||||
class ExerciseViewModel(application: Application) : AndroidViewModel(application) {
|
|
||||||
data class YouTubeSessionParams(val url: String, val sourceLanguage: String?, val targetLanguage: String?)
|
data class YouTubeSessionParams(val url: String, val sourceLanguage: String?, val targetLanguage: String?)
|
||||||
companion object {
|
|
||||||
@SuppressLint("StaticFieldLeak")
|
@SuppressLint("StaticFieldLeak")
|
||||||
@Volatile private var INSTANCE: ExerciseViewModel? = null
|
val context = application.applicationContext!!
|
||||||
fun getInstance(application: Application): ExerciseViewModel = INSTANCE ?: synchronized(this) {
|
|
||||||
INSTANCE ?: ExerciseViewModel(application).also { INSTANCE = it }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun normalizeText(input: String?): String {
|
private fun normalizeText(input: String?): String {
|
||||||
if (input.isNullOrBlank()) return ""
|
if (input.isNullOrBlank()) return ""
|
||||||
@@ -141,18 +119,6 @@ class ExerciseViewModel(application: Application) : AndroidViewModel(application
|
|||||||
if (nk.isNotBlank() && nv.isNotBlank()) nk to nv else null
|
if (nk.isNotBlank() && nv.isNotBlank()) nk to nv else null
|
||||||
}.toMap()
|
}.toMap()
|
||||||
|
|
||||||
|
|
||||||
@SuppressLint("StaticFieldLeak")
|
|
||||||
val context = application.applicationContext!!
|
|
||||||
val exerciseRepository = ExerciseRepository(application.applicationContext)
|
|
||||||
val exerciseService = ExerciseService(application.applicationContext)
|
|
||||||
val vocabularyRepository = VocabularyRepository.getInstance(application.applicationContext)
|
|
||||||
|
|
||||||
// Translation helpers
|
|
||||||
private val languageRepository = LanguageRepository(application.applicationContext)
|
|
||||||
private val translationService = TranslationService(application.applicationContext)
|
|
||||||
private var currentSubtitleTranslationJob: Job? = null
|
|
||||||
|
|
||||||
private val _exercises = MutableStateFlow<List<Exercise>>(emptyList())
|
private val _exercises = MutableStateFlow<List<Exercise>>(emptyList())
|
||||||
val exercises: StateFlow<List<Exercise>> = _exercises.asStateFlow()
|
val exercises: StateFlow<List<Exercise>> = _exercises.asStateFlow()
|
||||||
|
|
||||||
@@ -169,7 +135,7 @@ class ExerciseViewModel(application: Application) : AndroidViewModel(application
|
|||||||
val youTubeExerciseState: StateFlow<YouTubeExerciseState> = _youTubeExerciseState.asStateFlow()
|
val youTubeExerciseState: StateFlow<YouTubeExerciseState> = _youTubeExerciseState.asStateFlow()
|
||||||
|
|
||||||
private var currentlyFetchingVideoId: String? = null
|
private var currentlyFetchingVideoId: String? = null
|
||||||
|
private var currentSubtitleTranslationJob: Job? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
loadExercisesFromRepository()
|
loadExercisesFromRepository()
|
||||||
@@ -412,7 +378,7 @@ class ExerciseViewModel(application: Application) : AndroidViewModel(application
|
|||||||
_youTubeExerciseState.value = YouTubeExerciseState.Loading
|
_youTubeExerciseState.value = YouTubeExerciseState.Loading
|
||||||
Log.d("ExerciseViewModel", "State set to Loading for videoId=$videoId")
|
Log.d("ExerciseViewModel", "State set to Loading for videoId=$videoId")
|
||||||
try {
|
try {
|
||||||
val title = YouTubeApiService.getVideoTitle(videoId)
|
val title = youTubeApiService.getVideoTitle(videoId)
|
||||||
Log.i("ExerciseViewModel", "Fetched video title: '${title.take(100)}'")
|
Log.i("ExerciseViewModel", "Fetched video title: '${title.take(100)}'")
|
||||||
|
|
||||||
var available: List<String> = emptyList()
|
var available: List<String> = emptyList()
|
||||||
@@ -424,7 +390,7 @@ class ExerciseViewModel(application: Application) : AndroidViewModel(application
|
|||||||
delay(2500)
|
delay(2500)
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
available = YouTubeExerciseService.listTranscripts(videoId)
|
available = youTubeExerciseService.listTranscripts(videoId)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.w("ExerciseViewModel", "listTranscripts failed on attempt $attempts", e)
|
Log.w("ExerciseViewModel", "listTranscripts failed on attempt $attempts", e)
|
||||||
}
|
}
|
||||||
@@ -441,7 +407,7 @@ class ExerciseViewModel(application: Application) : AndroidViewModel(application
|
|||||||
Log.d("ExerciseViewModel", "Chosen transcript language: $chosenLang (preferred was $langCode)")
|
Log.d("ExerciseViewModel", "Chosen transcript language: $chosenLang (preferred was $langCode)")
|
||||||
|
|
||||||
val finalLang = chosenLang ?: throw Exception("No transcripts available for this video.")
|
val finalLang = chosenLang ?: throw Exception("No transcripts available for this video.")
|
||||||
val subtitles = YouTubeExerciseService.getTranscript(videoId, finalLang)
|
val subtitles = youTubeExerciseService.getTranscript(videoId, finalLang)
|
||||||
Log.i("ExerciseViewModel", "Fetched ${subtitles.size} subtitle lines for $videoId in lang=${finalLang}")
|
Log.i("ExerciseViewModel", "Fetched ${subtitles.size} subtitle lines for $videoId in lang=${finalLang}")
|
||||||
|
|
||||||
_youTubeExerciseState.value = YouTubeExerciseState.Success(
|
_youTubeExerciseState.value = YouTubeExerciseState.Success(
|
||||||
@@ -616,4 +582,4 @@ class ExerciseViewModel(application: Application) : AndroidViewModel(application
|
|||||||
// Reset the exercise state to allow restarting
|
// Reset the exercise state to allow restarting
|
||||||
_youTubeExerciseState.value = YouTubeExerciseState.Idle
|
_youTubeExerciseState.value = YouTubeExerciseState.Idle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,41 +5,22 @@ package eu.gaudian.translator.viewmodel
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import eu.gaudian.translator.model.grammar.LanguageConfig
|
import eu.gaudian.translator.model.grammar.LanguageConfig
|
||||||
import eu.gaudian.translator.utils.Log
|
import eu.gaudian.translator.model.repository.LanguageConfigRepository
|
||||||
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.asStateFlow
|
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
import kotlinx.coroutines.flow.stateIn
|
import kotlinx.coroutines.flow.stateIn
|
||||||
import kotlinx.coroutines.launch
|
import javax.inject.Inject
|
||||||
import kotlinx.serialization.json.Json
|
|
||||||
|
|
||||||
/**
|
@HiltViewModel
|
||||||
* TODO: Convert LanguageConfigViewModel to HiltViewModel
|
class LanguageConfigViewModel @Inject constructor(
|
||||||
*
|
application: Application,
|
||||||
* This ViewModel needs to be converted to use Hilt for dependency injection.
|
private val languageConfigRepository: LanguageConfigRepository
|
||||||
* Currently it manually instantiates dependencies in the constructor.
|
) : AndroidViewModel(application) {
|
||||||
*
|
|
||||||
* Checklist:
|
|
||||||
* - [ ] Add @HiltViewModel annotation to the class
|
|
||||||
* - [ ] Change constructor to use @Inject and inject dependencies:
|
|
||||||
* - Application (provided by Hilt)
|
|
||||||
* - [ ] Remove manual dependency instantiation from constructor
|
|
||||||
* - [ ] Update all places where LanguageConfigViewModel() is instantiated to use Hilt injection
|
|
||||||
* - [ ] Test that the ViewModel works correctly with dependency injection
|
|
||||||
*/
|
|
||||||
class LanguageConfigViewModel(application: Application) : AndroidViewModel(application) {
|
|
||||||
|
|
||||||
private val _configs = MutableStateFlow<Map<String, LanguageConfig>>(emptyMap())
|
val configs: StateFlow<Map<String, LanguageConfig>> = languageConfigRepository.configs
|
||||||
val configs = _configs.asStateFlow()
|
|
||||||
|
|
||||||
private val jsonParser = Json { ignoreUnknownKeys = true }
|
|
||||||
|
|
||||||
init {
|
|
||||||
loadAllConfigs()
|
|
||||||
}
|
|
||||||
|
|
||||||
val allWordClasses: StateFlow<List<String>> = configs.map { configsMap ->
|
val allWordClasses: StateFlow<List<String>> = configs.map { configsMap ->
|
||||||
configsMap.values
|
configsMap.values
|
||||||
@@ -48,44 +29,15 @@ class LanguageConfigViewModel(application: Application) : AndroidViewModel(appli
|
|||||||
.sorted()
|
.sorted()
|
||||||
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
|
}.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
|
||||||
|
|
||||||
/**
|
|
||||||
* Loads all language configuration files from the "language_configs" asset directory.
|
|
||||||
*/
|
|
||||||
private fun loadAllConfigs() {
|
|
||||||
viewModelScope.launch {
|
|
||||||
val context = getApplication<Application>().applicationContext
|
|
||||||
try {
|
|
||||||
val configFiles = context.assets.list("language_configs") ?: return@launch
|
|
||||||
val loadedConfigs = mutableMapOf<String, LanguageConfig>()
|
|
||||||
|
|
||||||
for (fileName in configFiles) {
|
|
||||||
if (fileName.endsWith(".json")) {
|
|
||||||
val jsonString = context.assets.open("language_configs/$fileName")
|
|
||||||
.bufferedReader()
|
|
||||||
.use { it.readText() }
|
|
||||||
|
|
||||||
val config = jsonParser.decodeFromString<LanguageConfig>(jsonString)
|
|
||||||
loadedConfigs[config.language_code] = config
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_configs.value = loadedConfigs
|
|
||||||
} catch (e: Exception) {
|
|
||||||
// Log the error for debugging, but don't crash the app
|
|
||||||
Log.e("Failed to load language configs: ${e.message}")
|
|
||||||
e.printStackTrace()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Retrieves the configuration for a specific language code.
|
* Retrieves the configuration for a specific language code.
|
||||||
*
|
*
|
||||||
* @param langCode The ISO language code (e.g., "de").
|
* @param langCode The ISO language code (e.g., "de").
|
||||||
* @return The LanguageConfig for the given code, or null if not found.
|
* @return The LanguageConfig for the given code, or null if not found.
|
||||||
*/
|
*/
|
||||||
|
@Suppress("unused")
|
||||||
fun getConfigForLanguage(langCode: String): LanguageConfig? {
|
fun getConfigForLanguage(langCode: String): LanguageConfig? {
|
||||||
Log.d("Fetching config for language: $langCode")
|
return languageConfigRepository.getConfigForLanguage(langCode)
|
||||||
return _configs.value[langCode]
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -97,12 +49,6 @@ class LanguageConfigViewModel(application: Application) : AndroidViewModel(appli
|
|||||||
* the language code is not found or if the language has no articles defined.
|
* the language code is not found or if the language has no articles defined.
|
||||||
*/
|
*/
|
||||||
fun getArticlesForLanguage(langCode: String): Set<String> {
|
fun getArticlesForLanguage(langCode: String): Set<String> {
|
||||||
return try {
|
return languageConfigRepository.getArticlesForLanguage(langCode)
|
||||||
val config = _configs.value[langCode]
|
|
||||||
config?.articles?.toSet() ?: emptySet()
|
|
||||||
} catch (e: Exception) {
|
|
||||||
Log.e("Error retrieving articles for '$langCode': ${e.message}")
|
|
||||||
emptySet()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,14 +29,12 @@ import javax.inject.Inject
|
|||||||
*/
|
*/
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class LanguageViewModel @Inject constructor(
|
class LanguageViewModel @Inject constructor(
|
||||||
application: Application
|
application: Application,
|
||||||
|
private val languageRepository: LanguageRepository
|
||||||
) : AndroidViewModel(application) {
|
) : AndroidViewModel(application) {
|
||||||
|
|
||||||
val languageRepository = LanguageRepository(application)
|
|
||||||
|
|
||||||
private val languageSwitchMutex = Mutex()
|
private val languageSwitchMutex = Mutex()
|
||||||
|
|
||||||
|
|
||||||
// Enabled languages (visible across the app)
|
// Enabled languages (visible across the app)
|
||||||
val allLanguages: StateFlow<List<Language>> = languageRepository.loadLanguages(LanguageListType.ALL)
|
val allLanguages: StateFlow<List<Language>> = languageRepository.loadLanguages(LanguageListType.ALL)
|
||||||
.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())
|
.stateIn(viewModelScope, SharingStarted.Eagerly, emptyList())
|
||||||
@@ -68,8 +66,6 @@ class LanguageViewModel @Inject constructor(
|
|||||||
|
|
||||||
|
|
||||||
suspend fun getLanguageById(id: Int): Language {
|
suspend fun getLanguageById(id: Int): Language {
|
||||||
|
|
||||||
|
|
||||||
val languages = allLanguages.first { it.isNotEmpty() }
|
val languages = allLanguages.first { it.isNotEmpty() }
|
||||||
|
|
||||||
val language = languages.find { it.nameResId == id }
|
val language = languages.find { it.nameResId == id }
|
||||||
@@ -99,13 +95,13 @@ class LanguageViewModel @Inject constructor(
|
|||||||
fun switchLanguages() {
|
fun switchLanguages() {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
languageSwitchMutex.withLock {
|
languageSwitchMutex.withLock {
|
||||||
val source = selectedSourceLanguage.value
|
val source = selectedSourceLanguage.value
|
||||||
val target = selectedTargetLanguage.value
|
val target = selectedTargetLanguage.value
|
||||||
languageRepository.saveSelectedSourceLanguage(target)
|
languageRepository.saveSelectedSourceLanguage(target)
|
||||||
languageRepository.saveSelectedTargetLanguage(source)
|
languageRepository.saveSelectedTargetLanguage(source)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
fun setSelectedSourceLanguage(languageId: Int?) {
|
fun setSelectedSourceLanguage(languageId: Int?) {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
@@ -193,4 +189,4 @@ class LanguageViewModel @Inject constructor(
|
|||||||
languageRepository.editCustomLanguage(languageId, newName, newCode, newRegion)
|
languageRepository.editCustomLanguage(languageId, newName, newCode, newRegion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,8 +2,8 @@ package eu.gaudian.translator.viewmodel
|
|||||||
|
|
||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.application
|
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
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.model.repository.SettingsRepository
|
import eu.gaudian.translator.model.repository.SettingsRepository
|
||||||
@@ -26,6 +26,7 @@ import kotlinx.datetime.minus
|
|||||||
import kotlinx.datetime.toLocalDateTime
|
import kotlinx.datetime.toLocalDateTime
|
||||||
import java.text.DateFormatSymbols
|
import java.text.DateFormatSymbols
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
|
||||||
data class CategoryProgress(
|
data class CategoryProgress(
|
||||||
@@ -53,38 +54,13 @@ data class StageStats(
|
|||||||
val itemCount: Int
|
val itemCount: Int
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@Suppress("SameParameterValue")
|
||||||
/**
|
@HiltViewModel
|
||||||
* TODO: Convert ProgressViewModel to HiltViewModel
|
class ProgressViewModel @Inject constructor(
|
||||||
*
|
application: Application,
|
||||||
* This ViewModel needs to be converted to use Hilt for dependency injection.
|
private val vocabularyRepository: VocabularyRepository,
|
||||||
* Currently it manually instantiates dependencies and uses a singleton pattern.
|
private val settingsRepository: SettingsRepository
|
||||||
*
|
) : AndroidViewModel(application) {
|
||||||
* Checklist:
|
|
||||||
* - [ ] Add @HiltViewModel annotation to the class
|
|
||||||
* - [ ] Change constructor to use @Inject and inject dependencies:
|
|
||||||
* - VocabularyRepository
|
|
||||||
* - SettingsRepository
|
|
||||||
* - [ ] Create/update RepositoryModule.kt to provide singleton instances of:
|
|
||||||
* - VocabularyRepository
|
|
||||||
* - SettingsRepository
|
|
||||||
* - [ ] Modify companion object getInstance() method to use Hilt's EntryPoint system
|
|
||||||
* - [ ] Create ProgressViewModelEntryPoint interface for accessing the singleton instance
|
|
||||||
* - [ ] Remove manual dependency instantiation from constructor and init block
|
|
||||||
* - [ ] Update all places where ProgressViewModel.getInstance() is called to ensure compatibility
|
|
||||||
* - [ ] Test that the ViewModel works correctly with dependency injection
|
|
||||||
*/
|
|
||||||
class ProgressViewModel(application: Application) : AndroidViewModel(application) {
|
|
||||||
companion object {
|
|
||||||
@Volatile private var INSTANCE: ProgressViewModel? = null
|
|
||||||
fun getInstance(application: Application): ProgressViewModel = INSTANCE ?: synchronized(this) {
|
|
||||||
INSTANCE ?: ProgressViewModel(application).also { INSTANCE = it }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
val vocabularyRepository = VocabularyRepository.getInstance(application.applicationContext)
|
|
||||||
val settingsRepository = SettingsRepository(application.applicationContext)
|
|
||||||
|
|
||||||
// Single-flight + coalescing scheduler for refresh()
|
// Single-flight + coalescing scheduler for refresh()
|
||||||
private val refreshMutex = Mutex()
|
private val refreshMutex = Mutex()
|
||||||
@@ -204,7 +180,6 @@ class ProgressViewModel(application: Application) : AndroidViewModel(application
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getWeeklyActivityStats(): List<WeeklyActivityStat> {
|
suspend fun getWeeklyActivityStats(): List<WeeklyActivityStat> {
|
||||||
val vocabularyRepository = VocabularyRepository.getInstance(application)
|
|
||||||
val today = kotlin.time.Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).date
|
val today = kotlin.time.Clock.System.now().toLocalDateTime(TimeZone.currentSystemDefault()).date
|
||||||
|
|
||||||
// 1. Create a list of the last 7 dates in chronological order.
|
// 1. Create a list of the last 7 dates in chronological order.
|
||||||
@@ -314,4 +289,4 @@ class ProgressViewModel(application: Application) : AndroidViewModel(application
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package eu.gaudian.translator.viewmodel
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import eu.gaudian.translator.utils.Log
|
import eu.gaudian.translator.utils.Log
|
||||||
import eu.gaudian.translator.utils.StatusAction
|
import eu.gaudian.translator.utils.StatusAction
|
||||||
import eu.gaudian.translator.utils.StatusMessageService
|
import eu.gaudian.translator.utils.StatusMessageService
|
||||||
@@ -17,6 +18,7 @@ import kotlinx.coroutines.flow.asStateFlow
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.util.LinkedList
|
import java.util.LinkedList
|
||||||
import java.util.Queue
|
import java.util.Queue
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
enum class MessageAction {
|
enum class MessageAction {
|
||||||
NAVIGATE_TO_API_KEYS
|
NAVIGATE_TO_API_KEYS
|
||||||
@@ -43,31 +45,11 @@ enum class MessageDisplayType(val priority: Int) {
|
|||||||
ACTIONABLE_ERROR(5)
|
ACTIONABLE_ERROR(5)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@HiltViewModel
|
||||||
* TODO: Convert StatusViewModel to HiltViewModel
|
class StatusViewModel @Inject constructor(
|
||||||
*
|
application: Application,
|
||||||
* This ViewModel needs to be converted to use Hilt for dependency injection.
|
private val statusMessageService: StatusMessageService
|
||||||
* Currently it manually instantiates dependencies and uses a singleton pattern.
|
) : AndroidViewModel(application) {
|
||||||
*
|
|
||||||
* Checklist:
|
|
||||||
* - [ ] Add @HiltViewModel annotation to the class
|
|
||||||
* - [ ] Change constructor to use @Inject and inject dependencies:
|
|
||||||
* - StatusMessageService
|
|
||||||
* - [ ] Create/update RepositoryModule.kt to provide singleton instances of:
|
|
||||||
* - StatusMessageService
|
|
||||||
* - [ ] Modify companion object getInstance() method to use Hilt's EntryPoint system
|
|
||||||
* - [ ] Create StatusViewModelEntryPoint interface for accessing the singleton instance
|
|
||||||
* - [ ] Remove manual dependency instantiation from constructor and init block
|
|
||||||
* - [ ] Update all places where StatusViewModel.getInstance() is called to ensure compatibility
|
|
||||||
* - [ ] Test that the ViewModel works correctly with dependency injection
|
|
||||||
*/
|
|
||||||
class StatusViewModel(application: Application) : AndroidViewModel(application) {
|
|
||||||
companion object {
|
|
||||||
@Volatile private var INSTANCE: StatusViewModel? = null
|
|
||||||
fun getInstance(application: Application): StatusViewModel = INSTANCE ?: synchronized(this) {
|
|
||||||
INSTANCE ?: StatusViewModel(application).also { INSTANCE = it }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private val messageQueue: Queue<Pair<String, MessageDisplayType>> = LinkedList()
|
private val messageQueue: Queue<Pair<String, MessageDisplayType>> = LinkedList()
|
||||||
private var messageDisplayJob: Job? = null
|
private var messageDisplayJob: Job? = null
|
||||||
@@ -79,7 +61,7 @@ class StatusViewModel(application: Application) : AndroidViewModel(application)
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
StatusMessageService.actions.collect { action ->
|
statusMessageService.actions.collect { action ->
|
||||||
handleAction(action)
|
handleAction(action)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -100,12 +82,10 @@ class StatusViewModel(application: Application) : AndroidViewModel(application)
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun showApiKeyMissingMessage() = viewModelScope.launch {
|
fun showApiKeyMissingMessage() = viewModelScope.launch {
|
||||||
StatusMessageService.trigger(
|
statusMessageService.showActionableMessage(
|
||||||
StatusAction.ShowActionableMessage(
|
text = "API Key is missing or invalid.",
|
||||||
text = "API Key is missing or invalid.",
|
type = MessageDisplayType.ACTIONABLE_ERROR,
|
||||||
type = MessageDisplayType.ACTIONABLE_ERROR,
|
action = MessageAction.NAVIGATE_TO_API_KEYS
|
||||||
action = MessageAction.NAVIGATE_TO_API_KEYS
|
|
||||||
)
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -120,43 +100,43 @@ class StatusViewModel(application: Application) : AndroidViewModel(application)
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun showPermanentMessage(message: String, type: MessageDisplayType) = viewModelScope.launch {
|
fun showPermanentMessage(message: String, type: MessageDisplayType) = viewModelScope.launch {
|
||||||
StatusMessageService.trigger(StatusAction.ShowPermanentMessage(message, type))
|
statusMessageService.showPermanentMessage(message, type)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cancelPermanentMessage() = viewModelScope.launch {
|
fun cancelPermanentMessage() = viewModelScope.launch {
|
||||||
StatusMessageService.trigger(StatusAction.CancelPermanentMessage)
|
statusMessageService.cancelPermanentMessage()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun performLoadingOperation(block: suspend () -> Unit) = viewModelScope.launch {
|
fun performLoadingOperation(block: suspend () -> Unit) = viewModelScope.launch {
|
||||||
StatusMessageService.trigger(StatusAction.PerformLoadingOperation(block))
|
statusMessageService.trigger(StatusAction.PerformLoadingOperation(block))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cancelLoadingOperation() = viewModelScope.launch {
|
fun cancelLoadingOperation() = viewModelScope.launch {
|
||||||
StatusMessageService.trigger(StatusAction.CancelLoadingOperation)
|
statusMessageService.trigger(StatusAction.CancelLoadingOperation)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showInfoMessage(message: String, timeoutInSeconds: Int = 3) = viewModelScope.launch {
|
fun showInfoMessage(message: String, timeoutInSeconds: Int = 3) = viewModelScope.launch {
|
||||||
StatusMessageService.trigger(StatusAction.ShowMessage(message, MessageDisplayType.INFO, timeoutInSeconds))
|
statusMessageService.showInfoMessage(message, timeoutInSeconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showLoadingMessage(message: String, timeoutInSeconds: Int = 0) = viewModelScope.launch { // Default timeout 0 for indefinite
|
fun showLoadingMessage(message: String, timeoutInSeconds: Int = 0) = viewModelScope.launch { // Default timeout 0 for indefinite
|
||||||
StatusMessageService.trigger(StatusAction.ShowMessage(message, MessageDisplayType.LOADING, timeoutInSeconds))
|
statusMessageService.showLoadingMessage(message, timeoutInSeconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showErrorMessage(message: String, timeoutInSeconds: Int = 5) = viewModelScope.launch { // Default timeout 5 for errors
|
fun showErrorMessage(message: String, timeoutInSeconds: Int = 5) = viewModelScope.launch { // Default timeout 5 for errors
|
||||||
StatusMessageService.trigger(StatusAction.ShowMessage(message, MessageDisplayType.ERROR, timeoutInSeconds))
|
statusMessageService.showErrorMessage(message, timeoutInSeconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun showSuccessMessage(message: String, timeoutInSeconds: Int = 3) = viewModelScope.launch {
|
fun showSuccessMessage(message: String, timeoutInSeconds: Int = 3) = viewModelScope.launch {
|
||||||
StatusMessageService.trigger(StatusAction.ShowMessage(message, MessageDisplayType.SUCCESS, timeoutInSeconds))
|
statusMessageService.showSuccessMessage(message, timeoutInSeconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun hideMessageBar() = viewModelScope.launch {
|
fun hideMessageBar() = viewModelScope.launch {
|
||||||
StatusMessageService.trigger(StatusAction.HideMessageBar)
|
statusMessageService.hideMessageBar()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cancelAllMessages() = viewModelScope.launch {
|
fun cancelAllMessages() = viewModelScope.launch {
|
||||||
StatusMessageService.trigger(StatusAction.CancelAllMessages)
|
statusMessageService.cancelAllMessages()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun cancelPermanentMessageInternal() {
|
private fun cancelPermanentMessageInternal() {
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ package eu.gaudian.translator.viewmodel
|
|||||||
import android.app.Application
|
import android.app.Application
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import eu.gaudian.translator.model.LanguageModel
|
import eu.gaudian.translator.model.LanguageModel
|
||||||
import eu.gaudian.translator.model.TranslationHistoryItem
|
import eu.gaudian.translator.model.TranslationHistoryItem
|
||||||
import eu.gaudian.translator.model.repository.ApiRepository
|
import eu.gaudian.translator.model.repository.ApiRepository
|
||||||
@@ -20,33 +21,17 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
@HiltViewModel
|
||||||
* TODO: Convert TranslationViewModel to HiltViewModel
|
class TranslationViewModel @Inject constructor(
|
||||||
*
|
application: Application,
|
||||||
* This ViewModel needs to be converted to use Hilt for dependency injection.
|
private val translationService: TranslationService,
|
||||||
* Currently it manually instantiates dependencies in the constructor.
|
private val apiRepository: ApiRepository,
|
||||||
*
|
val languageRepository: LanguageRepository
|
||||||
* Checklist:
|
) : AndroidViewModel(application) {
|
||||||
* - [ ] Add @HiltViewModel annotation to the class
|
|
||||||
* - [ ] Change constructor to use @Inject and inject dependencies:
|
|
||||||
* - TranslationService
|
|
||||||
* - ApiRepository
|
|
||||||
* - TextToSpeechHelper (if needed)
|
|
||||||
* - [ ] Create/update RepositoryModule.kt to provide singleton instances of:
|
|
||||||
* - TranslationService
|
|
||||||
* - ApiRepository
|
|
||||||
* - [ ] Modify companion object getInstance() method to use Hilt's EntryPoint system
|
|
||||||
* - [ ] Create TranslationViewModelEntryPoint interface for accessing the singleton instance
|
|
||||||
* - [ ] Remove manual dependency instantiation from constructor and init block
|
|
||||||
* - [ ] Update all places where TranslationViewModel.getInstance() is called to ensure compatibility
|
|
||||||
* - [ ] Test that the ViewModel works correctly with dependency injection
|
|
||||||
*/
|
|
||||||
class TranslationViewModel(application: Application) : AndroidViewModel(application) {
|
|
||||||
|
|
||||||
// For back/forward navigation of history in the UI (like editors)
|
// For back/forward navigation of history in the UI (like editors)
|
||||||
val languageRepository = LanguageRepository(application)
|
|
||||||
|
|
||||||
private val _historyCursor = MutableStateFlow(-1)
|
private val _historyCursor = MutableStateFlow(-1)
|
||||||
|
|
||||||
private val _canGoBack = MutableStateFlow(false)
|
private val _canGoBack = MutableStateFlow(false)
|
||||||
@@ -57,9 +42,6 @@ class TranslationViewModel(application: Application) : AndroidViewModel(applicat
|
|||||||
|
|
||||||
val historyCursor: StateFlow<Int> get() = _historyCursor
|
val historyCursor: StateFlow<Int> get() = _historyCursor
|
||||||
|
|
||||||
private val translationService = TranslationService(application)
|
|
||||||
private val apiRepository = ApiRepository(application)
|
|
||||||
|
|
||||||
private val _inputText = MutableStateFlow("")
|
private val _inputText = MutableStateFlow("")
|
||||||
val inputText: StateFlow<String> get() = _inputText
|
val inputText: StateFlow<String> get() = _inputText
|
||||||
|
|
||||||
@@ -341,4 +323,4 @@ class TranslationViewModel(application: Application) : AndroidViewModel(applicat
|
|||||||
}
|
}
|
||||||
updateBackForwardState()
|
updateBackForwardState()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,6 +5,8 @@ import androidx.lifecycle.AndroidViewModel
|
|||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import eu.gaudian.translator.model.VocabularyItem
|
import eu.gaudian.translator.model.VocabularyItem
|
||||||
|
import eu.gaudian.translator.model.repository.LanguageConfigRepository
|
||||||
|
import eu.gaudian.translator.model.repository.LanguageRepository
|
||||||
import eu.gaudian.translator.model.repository.VocabularyRepository
|
import eu.gaudian.translator.model.repository.VocabularyRepository
|
||||||
import eu.gaudian.translator.utils.Log
|
import eu.gaudian.translator.utils.Log
|
||||||
import eu.gaudian.translator.utils.StringHelper
|
import eu.gaudian.translator.utils.StringHelper
|
||||||
@@ -54,14 +56,11 @@ data class ExerciseResults(
|
|||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class VocabularyExerciseViewModel @Inject constructor(
|
class VocabularyExerciseViewModel @Inject constructor(
|
||||||
application: Application,
|
application: Application,
|
||||||
|
private val vocabularyRepository: VocabularyRepository,
|
||||||
|
private val languageConfigRepository: LanguageConfigRepository,
|
||||||
|
private val languageRepository: LanguageRepository
|
||||||
) : AndroidViewModel(application) {
|
) : AndroidViewModel(application) {
|
||||||
|
|
||||||
private val vocabularyRepository = VocabularyRepository.getInstance(application)
|
|
||||||
|
|
||||||
|
|
||||||
private val languageConfigViewModel = LanguageConfigViewModel(application)
|
|
||||||
private val languageViewModel = LanguageViewModel(application)
|
|
||||||
|
|
||||||
private val _exerciseState = MutableStateFlow<VocabularyExerciseState?>(null)
|
private val _exerciseState = MutableStateFlow<VocabularyExerciseState?>(null)
|
||||||
val exerciseState: StateFlow<VocabularyExerciseState?> = _exerciseState.asStateFlow()
|
val exerciseState: StateFlow<VocabularyExerciseState?> = _exerciseState.asStateFlow()
|
||||||
|
|
||||||
@@ -232,7 +231,7 @@ class VocabularyExerciseViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Item matches, determine if we need to switch
|
// Item matches, determine if we need to switch
|
||||||
// Switch if origin is specified and it's not the first language, or if target is specified and it IS the first language
|
// if origin is specified, and it's not the first language, or if target is specified and it IS the first language
|
||||||
(config.originLanguageId != null && config.originLanguageId != itemToUse.languageFirstId) ||
|
(config.originLanguageId != null && config.originLanguageId != itemToUse.languageFirstId) ||
|
||||||
(config.originLanguageId == null && config.targetLanguageId != null && config.targetLanguageId == itemToUse.languageFirstId)
|
(config.originLanguageId == null && config.targetLanguageId != null && config.targetLanguageId == itemToUse.languageFirstId)
|
||||||
}
|
}
|
||||||
@@ -355,10 +354,11 @@ class VocabularyExerciseViewModel @Inject constructor(
|
|||||||
is VocabularyExerciseState.Spelling -> {
|
is VocabularyExerciseState.Spelling -> {
|
||||||
val userAnswer = (answer as String).trim()
|
val userAnswer = (answer as String).trim()
|
||||||
val languageId = if (state.isSwitched) state.item.languageFirstId else state.item.languageSecondId
|
val languageId = if (state.isSwitched) state.item.languageFirstId else state.item.languageSecondId
|
||||||
val language = languageViewModel.getLanguageById(languageId ?: 0)
|
val language = languageRepository.getLanguageById(languageId ?: 0)
|
||||||
|
?: return@launch
|
||||||
|
|
||||||
// Get articles for the language
|
// Get articles for the language
|
||||||
val articles = languageConfigViewModel.getArticlesForLanguage(language.code)
|
val articles = languageConfigRepository.getArticlesForLanguage(language.code)
|
||||||
|
|
||||||
// Normalize and split possible answers using helper
|
// Normalize and split possible answers using helper
|
||||||
val normalizedCorrectAnswer = StringHelper.normalizeAnswer(correctAnswer)
|
val normalizedCorrectAnswer = StringHelper.normalizeAnswer(correctAnswer)
|
||||||
@@ -484,6 +484,4 @@ class VocabularyExerciseViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import androidx.activity.result.ActivityResultLauncher
|
|||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import eu.gaudian.translator.model.CardSet
|
import eu.gaudian.translator.model.CardSet
|
||||||
import eu.gaudian.translator.model.Language
|
import eu.gaudian.translator.model.Language
|
||||||
import eu.gaudian.translator.model.VocabularyCategory
|
import eu.gaudian.translator.model.VocabularyCategory
|
||||||
@@ -18,11 +19,14 @@ import eu.gaudian.translator.model.VocabularyGrammarDetails
|
|||||||
import eu.gaudian.translator.model.VocabularyItem
|
import eu.gaudian.translator.model.VocabularyItem
|
||||||
import eu.gaudian.translator.model.VocabularyStage
|
import eu.gaudian.translator.model.VocabularyStage
|
||||||
import eu.gaudian.translator.model.jsonParser
|
import eu.gaudian.translator.model.jsonParser
|
||||||
|
import eu.gaudian.translator.model.repository.LanguageConfigRepository
|
||||||
import eu.gaudian.translator.model.repository.LanguageListType
|
import eu.gaudian.translator.model.repository.LanguageListType
|
||||||
import eu.gaudian.translator.model.repository.LanguageRepository
|
import eu.gaudian.translator.model.repository.LanguageRepository
|
||||||
import eu.gaudian.translator.model.repository.VocabularyFileSaver
|
import eu.gaudian.translator.model.repository.VocabularyFileSaver
|
||||||
import eu.gaudian.translator.model.repository.VocabularyRepository
|
import eu.gaudian.translator.model.repository.VocabularyRepository
|
||||||
import eu.gaudian.translator.utils.Log
|
import eu.gaudian.translator.utils.Log
|
||||||
|
import eu.gaudian.translator.utils.StatusAction
|
||||||
|
import eu.gaudian.translator.utils.StatusMessageService
|
||||||
import eu.gaudian.translator.utils.StringHelper
|
import eu.gaudian.translator.utils.StringHelper
|
||||||
import eu.gaudian.translator.utils.VocabularyService
|
import eu.gaudian.translator.utils.VocabularyService
|
||||||
import eu.gaudian.translator.utils.dictionary.DictionaryService
|
import eu.gaudian.translator.utils.dictionary.DictionaryService
|
||||||
@@ -47,38 +51,20 @@ import kotlinx.coroutines.flow.stateIn
|
|||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
import javax.inject.Inject
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
import kotlin.time.ExperimentalTime
|
import kotlin.time.ExperimentalTime
|
||||||
|
|
||||||
/**
|
@HiltViewModel
|
||||||
* TODO: Convert VocabularyViewModel to HiltViewModel
|
class VocabularyViewModel @Inject constructor(
|
||||||
*
|
application: Application,
|
||||||
* This ViewModel needs to be converted to use Hilt for dependency injection.
|
private val vocabularyRepository: VocabularyRepository,
|
||||||
* Currently it manually instantiates dependencies in the constructor and companion object.
|
private val languageRepository: LanguageRepository,
|
||||||
*
|
private val languageConfigRepository: LanguageConfigRepository,
|
||||||
* Checklist:
|
private val vocabularyItemService: VocabularyService,
|
||||||
* - [ ] Add @HiltViewModel annotation to the class
|
private val dictionaryService: DictionaryService,
|
||||||
* - [ ] Change constructor to use @Inject and inject dependencies:
|
private val statusService: StatusMessageService,
|
||||||
* - ApiManager
|
) : AndroidViewModel(application) {
|
||||||
* - ApiRepository
|
|
||||||
* - SettingsRepository
|
|
||||||
* - ApiLogRepository
|
|
||||||
* - VocabularyRepository
|
|
||||||
* - LanguageRepository
|
|
||||||
* - VocabularyService
|
|
||||||
* - StatusViewModel (consider if this should be injected or accessed differently)
|
|
||||||
* - LanguageConfigViewModel (consider if this should be injected or accessed differently)
|
|
||||||
* - [ ] Create/update RepositoryModule.kt to provide singleton instances of:
|
|
||||||
* - VocabularyRepository
|
|
||||||
* - LanguageRepository
|
|
||||||
* - VocabularyService
|
|
||||||
* - [ ] Modify companion object getInstance() method to use Hilt's EntryPoint system
|
|
||||||
* - [ ] Create VocabularyViewModelEntryPoint interface for accessing the singleton instance
|
|
||||||
* - [ ] Remove manual dependency instantiation from constructor and init block
|
|
||||||
* - [ ] Update all places where VocabularyViewModel.getInstance() is called to ensure compatibility
|
|
||||||
* - [ ] Test that the ViewModel works correctly with dependency injection
|
|
||||||
*/
|
|
||||||
class VocabularyViewModel(application: Application) : AndroidViewModel(application) {
|
|
||||||
fun extractUniqueWords(text: String): List<String> {
|
fun extractUniqueWords(text: String): List<String> {
|
||||||
if (text.isBlank()) return emptyList()
|
if (text.isBlank()) return emptyList()
|
||||||
val words = text.lowercase()
|
val words = text.lowercase()
|
||||||
@@ -95,48 +81,35 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
val src = languageRepository.loadSelectedSourceLanguage().first()
|
val src = languageRepository.loadSelectedSourceLanguage().first()
|
||||||
val tgt = languageRepository.loadSelectedTargetLanguage().first()
|
val tgt = languageRepository.loadSelectedTargetLanguage().first()
|
||||||
if (src == null || tgt == null) {
|
if (src == null || tgt == null) {
|
||||||
statusViewModel.showErrorMessage("Source and target languages must be selected.")
|
statusService.showErrorMessage("Source and target languages must be selected.")
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
val words = extractUniqueWords(text)
|
val words = extractUniqueWords(text)
|
||||||
if (words.isEmpty()) {
|
if (words.isEmpty()) {
|
||||||
statusViewModel.showErrorMessage("No words found in the provided text.")
|
statusService.showErrorMessage("No words found in the provided text.")
|
||||||
return@launch
|
return@launch
|
||||||
}
|
}
|
||||||
_isGenerating.value = true
|
_isGenerating.value = true
|
||||||
statusViewModel.showLoadingMessage("Translating ${words.size} words…")
|
statusService.showLoadingMessage("Translating ${words.size} words…")
|
||||||
val result = vocabularyItemService.translateWordsBatch(words, src, tgt)
|
val result = vocabularyItemService.translateWordsBatch(words, src, tgt)
|
||||||
result.onSuccess { items ->
|
result.onSuccess { items ->
|
||||||
_generatedVocabularyItems.value = items
|
_generatedVocabularyItems.value = items
|
||||||
statusViewModel.cancelLoadingOperation()
|
statusService.trigger(StatusAction.CancelLoadingOperation)
|
||||||
}.onFailure { ex ->
|
}.onFailure { ex ->
|
||||||
statusViewModel.showErrorMessage("Failed to translate words: ${ex.message}")
|
statusService.showErrorMessage("Failed to translate words: ${ex.message}")
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
statusViewModel.showErrorMessage("Unexpected error: ${e.message}")
|
statusService.showErrorMessage("Unexpected error: ${e.message}")
|
||||||
} finally {
|
} finally {
|
||||||
_isGenerating.value = false
|
_isGenerating.value = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
companion object {
|
|
||||||
@Volatile private var INSTANCE: VocabularyViewModel? = null
|
|
||||||
fun getInstance(application: Application): VocabularyViewModel = INSTANCE ?: synchronized(this) {
|
|
||||||
INSTANCE ?: VocabularyViewModel(application).also { INSTANCE = it }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@Suppress("PrivatePropertyName")
|
@Suppress("PrivatePropertyName")
|
||||||
private val TAG = "VocabularyViewModel"
|
private val TAG = "VocabularyViewModel"
|
||||||
|
|
||||||
|
|
||||||
val statusViewModel = StatusViewModel.getInstance(application)
|
|
||||||
val languageConfigViewModel = LanguageConfigViewModel(application)
|
|
||||||
val vocabularyRepository = VocabularyRepository.getInstance(application)
|
|
||||||
val languageRepository = LanguageRepository(application)
|
|
||||||
|
|
||||||
private val vocabularyItemService = VocabularyService(application)
|
|
||||||
|
|
||||||
|
|
||||||
val vocabularyItems: StateFlow<List<VocabularyItem>> = vocabularyRepository.getAllVocabularyItemsFlow()
|
val vocabularyItems: StateFlow<List<VocabularyItem>> = vocabularyRepository.getAllVocabularyItemsFlow()
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyList())
|
||||||
|
|
||||||
@@ -171,8 +144,6 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
val stageMapping: StateFlow<Map<Int, VocabularyStage>> = vocabularyRepository.loadStageMapping()
|
val stageMapping: StateFlow<Map<Int, VocabularyStage>> = vocabularyRepository.loadStageMapping()
|
||||||
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyMap())
|
.stateIn(viewModelScope, SharingStarted.WhileSubscribed(5000), emptyMap())
|
||||||
|
|
||||||
val dictionaryService = DictionaryService(application)
|
|
||||||
|
|
||||||
private val _cardSet = MutableStateFlow<CardSet?>(null)
|
private val _cardSet = MutableStateFlow<CardSet?>(null)
|
||||||
val cardSet: StateFlow<CardSet?> = _cardSet.asStateFlow()
|
val cardSet: StateFlow<CardSet?> = _cardSet.asStateFlow()
|
||||||
|
|
||||||
@@ -223,7 +194,7 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
}
|
}
|
||||||
.values // We only need the groups of items, not the keys
|
.values // We only need the groups of items, not the keys
|
||||||
.filter { group -> group.size > 1 } // A group with more than one item signifies duplicates
|
.filter { group -> group.size > 1 } // A group with more than one item signifies duplicates
|
||||||
.flatten() // Convert the list of duplicate groups into a a single list of all duplicate items
|
.flatten() // Convert the list of duplicate groups into a single list of all duplicate items
|
||||||
}
|
}
|
||||||
.flowOn(Dispatchers.Default) // Perform this potentially heavy computation on a background thread
|
.flowOn(Dispatchers.Default) // Perform this potentially heavy computation on a background thread
|
||||||
.stateIn(
|
.stateIn(
|
||||||
@@ -300,7 +271,7 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
}
|
}
|
||||||
Log.d(TAG, "Successfully added vocabulary items to category in ${duration}ms")
|
Log.d(TAG, "Successfully added vocabulary items to category in ${duration}ms")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
statusViewModel.showErrorMessage("Error in addVocabularyItemToCategory: ${e.message}")
|
statusService.showErrorMessage("Error in addVocabularyItemToCategory: ${e.message}")
|
||||||
Log.e(TAG, "Error in addVocabularyItemToCategory: ${e.message}")
|
Log.e(TAG, "Error in addVocabularyItemToCategory: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -325,7 +296,7 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
}
|
}
|
||||||
Log.d(TAG, "Successfully removed vocabulary items from category in ${duration}ms")
|
Log.d(TAG, "Successfully removed vocabulary items from category in ${duration}ms")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
statusViewModel.showErrorMessage("Error removing items from category: ${e.message}")
|
statusService.showErrorMessage("Error removing items from category: ${e.message}")
|
||||||
Log.e(TAG, "Error in removeVocabularyItemsFromCategory: ${e.message}")
|
Log.e(TAG, "Error in removeVocabularyItemsFromCategory: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,7 +309,7 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
|
|
||||||
vocabularyRepository.changeVocabularyItemsStage(vocabularyItems, stage)
|
vocabularyRepository.changeVocabularyItemsStage(vocabularyItems, stage)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
statusViewModel.showErrorMessage("Error in addVocabularyItemToStage: ${e.message}")
|
statusService.showErrorMessage("Error in addVocabularyItemToStage: ${e.message}")
|
||||||
Log.e(TAG, "Error in addVocabularyItemToStage: ${e.message}")
|
Log.e(TAG, "Error in addVocabularyItemToStage: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -356,7 +327,7 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
"Successfully added ${items.size} new vocabulary items in ${duration}ms"
|
"Successfully added ${items.size} new vocabulary items in ${duration}ms"
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
statusViewModel.showErrorMessage("Error adding items: ${e.message}")
|
statusService.showErrorMessage("Error adding items: ${e.message}")
|
||||||
Log.e(TAG, "Error adding items: ${e.message}")
|
Log.e(TAG, "Error adding items: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -373,7 +344,7 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
}
|
}
|
||||||
Log.d(TAG, "Successfully deleted vocabulary items in ${duration}ms")
|
Log.d(TAG, "Successfully deleted vocabulary items in ${duration}ms")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
statusViewModel.showErrorMessage("Error deleting items: ${e.message}")
|
statusService.showErrorMessage("Error deleting items: ${e.message}")
|
||||||
Log.e(TAG, "Error deleting item: ${e.message}")
|
Log.e(TAG, "Error deleting item: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -389,7 +360,7 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
}
|
}
|
||||||
Log.d(TAG, "Successfully edited vocabulary item in ${duration}ms")
|
Log.d(TAG, "Successfully edited vocabulary item in ${duration}ms")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
statusViewModel.showErrorMessage("Error in editVocabularyItem: ${e.message}")
|
statusService.showErrorMessage("Error in editVocabularyItem: ${e.message}")
|
||||||
Log.e(TAG, "Error in editVocabularyItem: ${e.message}")
|
Log.e(TAG, "Error in editVocabularyItem: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -445,7 +416,7 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
// Placeholder: For now, this just deletes the new item.
|
// Placeholder: For now, this just deletes the new item.
|
||||||
// A full implementation would merge stages, categories, etc., and update the existing item based on the rules.
|
// A full implementation would merge stages, categories, etc., and update the existing item based on the rules.
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
statusViewModel.showSuccessMessage("Items merged!", 2)
|
statusService.showSuccessMessage("Items merged!", 2)
|
||||||
deleteVocabularyItemsById(listOf(newItem.id))
|
deleteVocabularyItemsById(listOf(newItem.id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -469,7 +440,7 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
}
|
}
|
||||||
Log.d(TAG, "deleteData of type $type completed in ${duration}ms")
|
Log.d(TAG, "deleteData of type $type completed in ${duration}ms")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
statusViewModel.showErrorMessage("Error in deleteData: ${e.message}")
|
statusService.showErrorMessage("Error in deleteData: ${e.message}")
|
||||||
Log.e(TAG, "Error in deleteData: ${e.message}")
|
Log.e(TAG, "Error in deleteData: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -723,7 +694,7 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
) {
|
) {
|
||||||
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 {
|
||||||
statusViewModel.showLoadingMessage("Loading card set")
|
statusService.showLoadingMessage("Loading card set")
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Step 1: Wait for vocabulary items to be loaded
|
// Step 1: Wait for vocabulary items to be loaded
|
||||||
@@ -753,7 +724,7 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
|
|
||||||
if (stageMappingMap.isEmpty()) {
|
if (stageMappingMap.isEmpty()) {
|
||||||
Log.w(TAG, "loadCardSet: Stage mapping still empty after $maxAttempts attempts")
|
Log.w(TAG, "loadCardSet: Stage mapping still empty after $maxAttempts attempts")
|
||||||
statusViewModel.showErrorMessage("Failed to initialize stage mappings")
|
statusService.showErrorMessage("Failed to initialize stage mappings")
|
||||||
_cardSet.value = null
|
_cardSet.value = null
|
||||||
return@launch
|
return@launch
|
||||||
} else {
|
} else {
|
||||||
@@ -795,15 +766,15 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
Log.d(TAG, "No items found for the specified filters")
|
Log.d(TAG, "No items found for the specified filters")
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
statusViewModel.showErrorMessage("Error loading card set: ${e.message}")
|
statusService.showErrorMessage("Error loading card set: ${e.message}")
|
||||||
_cardSet.value = null
|
_cardSet.value = null
|
||||||
Log.e(TAG, "Error in loadCardSet: ${e.message}")
|
Log.e(TAG, "Error in loadCardSet: ${e.message}")
|
||||||
}
|
}
|
||||||
|
|
||||||
statusViewModel.hideMessageBar()
|
statusService.hideMessageBar()
|
||||||
if (_cardSet.value == null) {
|
if (_cardSet.value == null) {
|
||||||
statusViewModel.cancelAllMessages()
|
statusService.cancelAllMessages()
|
||||||
statusViewModel.showErrorMessage("No cards found for the specified filter", 3)
|
statusService.showErrorMessage("No cards found for the specified filter", 3)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -818,7 +789,7 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
}
|
}
|
||||||
Log.d(TAG, "deleteVocabularyItemsByCategory for ID $categoryID took ${duration}ms")
|
Log.d(TAG, "deleteVocabularyItemsByCategory for ID $categoryID took ${duration}ms")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
statusViewModel.showErrorMessage("Error in deleteVocabularyItemsByCategory: ${e.message}")
|
statusService.showErrorMessage("Error in deleteVocabularyItemsByCategory: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -858,7 +829,7 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
|
|
||||||
fun saveRepositoryState() {
|
fun saveRepositoryState() {
|
||||||
if (!::saveFileLauncher.isInitialized) {
|
if (!::saveFileLauncher.isInitialized) {
|
||||||
statusViewModel.showErrorMessage("Save File Launcher not initialized.")
|
statusService.showErrorMessage("Save File Launcher not initialized.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val intent = fileSaver.createSaveDocumentIntent(fileSaver.generateFilename())
|
val intent = fileSaver.createSaveDocumentIntent(fileSaver.generateFilename())
|
||||||
@@ -867,7 +838,7 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
|
|
||||||
fun saveCategory(categoryId: Int) {
|
fun saveCategory(categoryId: Int) {
|
||||||
if (!::saveFileLauncher.isInitialized) {
|
if (!::saveFileLauncher.isInitialized) {
|
||||||
statusViewModel.showErrorMessage("Save File Launcher not initialized.")
|
statusService.showErrorMessage("Save File Launcher not initialized.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
val filename = fileSaver.generateFilenameForCategory(categoryId)
|
val filename = fileSaver.generateFilenameForCategory(categoryId)
|
||||||
@@ -897,11 +868,11 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} ?: run {
|
} ?: run {
|
||||||
statusViewModel.showErrorMessage("Error: No URI received from file picker.")
|
statusService.showErrorMessage("Error: No URI received from file picker.")
|
||||||
Log.e(TAG, "handleSaveResult: No URI received")
|
Log.e(TAG, "handleSaveResult: No URI received")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
statusViewModel.showErrorMessage("File save cancelled or failed.")
|
statusService.showErrorMessage("File save cancelled or failed.")
|
||||||
Log.d(TAG, "handleSaveResult: Save cancelled or failed")
|
Log.d(TAG, "handleSaveResult: Save cancelled or failed")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -916,10 +887,10 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
vocabularyRepository.introduceVocabularyItems(vocabularyItems)
|
vocabularyRepository.introduceVocabularyItems(vocabularyItems)
|
||||||
vocabularyRepository.cleanDuplicates()
|
vocabularyRepository.cleanDuplicates()
|
||||||
}
|
}
|
||||||
statusViewModel.showSuccessMessage("Vocabulary items imported successfully.")
|
statusService.showSuccessMessage("Vocabulary items imported successfully.")
|
||||||
Log.d(TAG, "Vocabulary import process took ${duration}ms")
|
Log.d(TAG, "Vocabulary import process took ${duration}ms")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
statusViewModel.showErrorMessage("Error importing vocabulary items: ${e.message}")
|
statusService.showErrorMessage("Error importing vocabulary items: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -928,9 +899,9 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
try {
|
try {
|
||||||
vocabularyRepository.wipeRepository()
|
vocabularyRepository.wipeRepository()
|
||||||
statusViewModel.showErrorMessage("All repository data deleted.")
|
statusService.showErrorMessage("All repository data deleted.")
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
statusViewModel.showErrorMessage("Failed to wipe repository: $e.message}")
|
statusService.showErrorMessage("Failed to wipe repository: $e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1075,7 +1046,7 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
}
|
}
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
statusViewModel.showLoadingMessage("Fetching grammar for ${items.size} items...")
|
statusService.showLoadingMessage("Fetching grammar for ${items.size} items...")
|
||||||
Log.d(TAG, "Attempting to fetch grammar details for a list of ${items.size} items.")
|
Log.d(TAG, "Attempting to fetch grammar details for a list of ${items.size} items.")
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@@ -1089,8 +1060,8 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
if (StringHelper.isSentenceStrict(item.wordFirst) || StringHelper.isSentenceStrict(item.wordSecond)) {
|
if (StringHelper.isSentenceStrict(item.wordFirst) || StringHelper.isSentenceStrict(item.wordSecond)) {
|
||||||
item // Skip article removal for sentences
|
item // Skip article removal for sentences
|
||||||
} else {
|
} else {
|
||||||
val articlesLangFirst = languagesMap[item.languageFirstId]?.code?.let { languageConfigViewModel.getArticlesForLanguage(it) } ?: emptySet()
|
val articlesLangFirst = languagesMap[item.languageFirstId]?.code?.let { languageConfigRepository.getArticlesForLanguage(it) } ?: emptySet()
|
||||||
val articlesLangSecond = languagesMap[item.languageSecondId]?.code?.let { languageConfigViewModel.getArticlesForLanguage(it) } ?: emptySet()
|
val articlesLangSecond = languagesMap[item.languageSecondId]?.code?.let { languageConfigRepository.getArticlesForLanguage(it) } ?: emptySet()
|
||||||
|
|
||||||
val allArticles = (articlesLangFirst + articlesLangSecond)
|
val allArticles = (articlesLangFirst + articlesLangSecond)
|
||||||
|
|
||||||
@@ -1110,16 +1081,16 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
|
|
||||||
// 5. Persist the updated items back to the database.
|
// 5. Persist the updated items back to the database.
|
||||||
vocabularyRepository.updateVocabularyItems(updatedItems)
|
vocabularyRepository.updateVocabularyItems(updatedItems)
|
||||||
statusViewModel.cancelLoadingOperation()
|
statusService.trigger(StatusAction.CancelLoadingOperation)
|
||||||
statusViewModel.showSuccessMessage("Grammar details updated!")
|
statusService.showSuccessMessage("Grammar details updated!")
|
||||||
|
|
||||||
}.onFailure { exception ->
|
}.onFailure { exception ->
|
||||||
Log.e(TAG, "Failed to fetch grammar details for list: ${exception.message}")
|
Log.e(TAG, "Failed to fetch grammar details for list: ${exception.message}")
|
||||||
statusViewModel.showErrorMessage("Could not retrieve grammar details.")
|
statusService.showErrorMessage("Could not retrieve grammar details.")
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(TAG, "An unexpected error occurred in fetchAndApplyGrammaticalDetailsForList: ${e.message}")
|
Log.e(TAG, "An unexpected error occurred in fetchAndApplyGrammaticalDetailsForList: ${e.message}")
|
||||||
statusViewModel.showErrorMessage("An unexpected error occurred.")
|
statusService.showErrorMessage("An unexpected error occurred.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1164,7 +1135,7 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
Log.d(TAG, "removeArticles: No articles found in item ${item.id}, no update needed.")
|
Log.d(TAG, "removeArticles: No articles found in item ${item.id}, no update needed.")
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
statusViewModel.showErrorMessage("Error removing articles: ${e.message}")
|
statusService.showErrorMessage("Error removing articles: ${e.message}")
|
||||||
Log.e(TAG, "Error in removeArticles: ${e.message}")
|
Log.e(TAG, "Error in removeArticles: ${e.message}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1267,14 +1238,14 @@ class VocabularyViewModel(application: Application) : AndroidViewModel(applicati
|
|||||||
|
|
||||||
if (itemsToUpdate.isNotEmpty()) {
|
if (itemsToUpdate.isNotEmpty()) {
|
||||||
vocabularyRepository.updateVocabularyItems(itemsToUpdate)
|
vocabularyRepository.updateVocabularyItems(itemsToUpdate)
|
||||||
statusViewModel.showSuccessMessage("Language ID updated for ${itemsToUpdate.size} items.")
|
statusService.showSuccessMessage("Language ID updated for ${itemsToUpdate.size} items.")
|
||||||
Log.d(TAG, "Successfully updated ${itemsToUpdate.size} items.")
|
Log.d(TAG, "Successfully updated ${itemsToUpdate.size} items.")
|
||||||
} else {
|
} else {
|
||||||
Log.d(TAG, "No items found with language ID $oldId to update.")
|
Log.d(TAG, "No items found with language ID $oldId to update.")
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
val errorMessage = "Error replacing language ID: ${e.message}"
|
val errorMessage = "Error replacing language ID: ${e.message}"
|
||||||
statusViewModel.showErrorMessage(errorMessage)
|
statusService.showErrorMessage(errorMessage)
|
||||||
Log.e(TAG, errorMessage)
|
Log.e(TAG, errorMessage)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,16 +21,6 @@ class SettingsViewModelTest {
|
|||||||
androidx.lifecycle.AndroidViewModel::class.java.isAssignableFrom(viewModelClass))
|
androidx.lifecycle.AndroidViewModel::class.java.isAssignableFrom(viewModelClass))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun `SettingsViewModel companion object has getInstance method`() {
|
|
||||||
// Given - Get the companion object
|
|
||||||
val companionObject = SettingsViewModel.Companion::class.java
|
|
||||||
|
|
||||||
// Then - Should have getInstance method
|
|
||||||
assertTrue("Companion should have getInstance method",
|
|
||||||
companionObject.methods.any { it.name == "getInstance" })
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `SettingsViewModel has expected constructor parameters`() {
|
fun `SettingsViewModel has expected constructor parameters`() {
|
||||||
// Given - Get the ViewModel class
|
// Given - Get the ViewModel class
|
||||||
|
|||||||
Reference in New Issue
Block a user