Compare commits
3 Commits
37d8518e50
...
b95a2de747
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b95a2de747 | ||
|
|
b65e16000c | ||
|
|
fdce6ba500 |
@@ -109,10 +109,19 @@ class ExampleSentenceRequest(
|
|||||||
override val requiredFields = listOf("word", "sourceSentence", "targetSentence")
|
override val requiredFields = listOf("word", "sourceSentence", "targetSentence")
|
||||||
|
|
||||||
init {
|
init {
|
||||||
promptBuilder.basePrompt = "Provide one short, simple and clear example sentence for the word '$word' in $languageFirst and fully translate the sentence to $languageSecond, using $wordTranslation as a translation."
|
promptBuilder.basePrompt = """
|
||||||
addDetail("Structure: { 'word': string, 'sourceSentence': string, 'targetSentence': string }.")
|
Task: Create a short, concise natural example sentence in $languageFirst for the word '$word'.
|
||||||
addDetail("Only include the fields above. Keep sentences concise and clear. Do not include any explanations or additional text.")
|
|
||||||
withJsonResponse("a JSON object with 'word' (the original word), 'sourceSentence' (the example sentence in the source language), and 'targetSentence' (the translation in the target language). Ensure all values are properly quoted strings.")
|
Rules:
|
||||||
|
1. The 'sourceSentence' must be entirely in $languageFirst.
|
||||||
|
2. Do NOT use the word '$wordTranslation' in the 'sourceSentence'.
|
||||||
|
3. The 'targetSentence' must be the $languageSecond translation of the 'sourceSentence'.
|
||||||
|
4. In the 'targetSentence', use the word '$wordTranslation'.
|
||||||
|
""".trimIndent()
|
||||||
|
|
||||||
|
addDetail("Constraint: Ensure 'sourceSentence' contains ONLY $languageFirst and 'targetSentence' contains ONLY $languageSecond.")
|
||||||
|
addDetail("Structure: { 'word': '$word', 'sourceSentence': string, 'targetSentence': string }.")
|
||||||
|
withJsonResponse("a JSON object. Ensure no mixed-language sentences occur.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -90,14 +90,14 @@ class DictionaryService(context: Context) {
|
|||||||
@OptIn(ExperimentalTime::class)
|
@OptIn(ExperimentalTime::class)
|
||||||
suspend fun searchDefinition(query: String, language: Language): Result<DictionaryEntry> = withContext(Dispatchers.IO) {
|
suspend fun searchDefinition(query: String, language: Language): Result<DictionaryEntry> = withContext(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
Log.i("DictionaryService", "Searching definition for word: $query in language: ${language.name}")
|
Log.i("DictionaryService", "Searching definition for word: $query in language: ${language.englishName}")
|
||||||
|
|
||||||
val requestedParts = getActivatedDictionaryOptions()
|
val requestedParts = getActivatedDictionaryOptions()
|
||||||
Log.d("DictionaryService", "Requested dictionary parts: $requestedParts")
|
Log.d("DictionaryService", "Requested dictionary parts: $requestedParts")
|
||||||
|
|
||||||
val template = DictionaryDefinitionRequest(
|
val template = DictionaryDefinitionRequest(
|
||||||
word = query,
|
word = query,
|
||||||
language = language.name,
|
language = language.englishName,
|
||||||
requestedParts = requestedParts
|
requestedParts = requestedParts
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -110,7 +110,7 @@ class DictionaryService(context: Context) {
|
|||||||
word = apiResponse.word,
|
word = apiResponse.word,
|
||||||
definition = apiResponse.parts,
|
definition = apiResponse.parts,
|
||||||
languageCode = language.nameResId,
|
languageCode = language.nameResId,
|
||||||
languageName = language.name,
|
languageName = language.englishName,
|
||||||
createdAt = Clock.System.now()
|
createdAt = Clock.System.now()
|
||||||
)
|
)
|
||||||
}.onFailure { exception ->
|
}.onFailure { exception ->
|
||||||
@@ -127,13 +127,13 @@ class DictionaryService(context: Context) {
|
|||||||
*/
|
*/
|
||||||
suspend fun getExampleSentence(word: String, wordTranslation: String, languageFirst: Language, languageSecond: Language): Result<Pair<String, String>> = withContext(Dispatchers.IO) {
|
suspend fun getExampleSentence(word: String, wordTranslation: String, languageFirst: Language, languageSecond: Language): Result<Pair<String, String>> = withContext(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
Log.i("DictionaryService", "Getting example sentence for word: $word (${languageFirst.name} -> ${languageSecond.name})")
|
Log.i("DictionaryService", "Getting example sentence for word: $word (${languageFirst.englishName} -> ${languageSecond.englishName})")
|
||||||
|
|
||||||
val template = ExampleSentenceRequest(
|
val template = ExampleSentenceRequest(
|
||||||
word = word,
|
word = word,
|
||||||
wordTranslation = wordTranslation,
|
wordTranslation = wordTranslation,
|
||||||
languageFirst = languageFirst.name,
|
languageFirst = languageFirst.englishName,
|
||||||
languageSecond = languageSecond.name
|
languageSecond = languageSecond.englishName
|
||||||
)
|
)
|
||||||
|
|
||||||
val result = apiRequestHandler.executeRequest(template)
|
val result = apiRequestHandler.executeRequest(template)
|
||||||
@@ -179,7 +179,7 @@ class DictionaryService(context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.i("DictionaryService", "Generating new word of the day for: $todayString in language: ${language.name}")
|
Log.i("DictionaryService", "Generating new word of the day for: $todayString in language: ${language.englishName}")
|
||||||
|
|
||||||
val topics = listOf(
|
val topics = listOf(
|
||||||
"science", "literature", "history", "technology", "nature",
|
"science", "literature", "history", "technology", "nature",
|
||||||
@@ -192,7 +192,7 @@ class DictionaryService(context: Context) {
|
|||||||
Log.d("DictionaryService", "Selected topic for word of the day: $randomTopic")
|
Log.d("DictionaryService", "Selected topic for word of the day: $randomTopic")
|
||||||
|
|
||||||
val template = WordOfTheDayRequest(
|
val template = WordOfTheDayRequest(
|
||||||
language = language.name,
|
language = language.englishName,
|
||||||
category = randomTopic
|
category = randomTopic
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -205,7 +205,7 @@ class DictionaryService(context: Context) {
|
|||||||
word = apiResponse.word,
|
word = apiResponse.word,
|
||||||
definition = apiResponse.parts,
|
definition = apiResponse.parts,
|
||||||
languageCode = language.nameResId,
|
languageCode = language.nameResId,
|
||||||
languageName = language.name,
|
languageName = language.englishName,
|
||||||
createdAt = today
|
createdAt = today
|
||||||
)
|
)
|
||||||
dictionaryRepository.saveWordOfTheDay(newEntry)
|
dictionaryRepository.saveWordOfTheDay(newEntry)
|
||||||
@@ -225,7 +225,7 @@ class DictionaryService(context: Context) {
|
|||||||
|
|
||||||
suspend fun getEtymology(query: String, language: Language): Result<EtymologyData> = withContext(Dispatchers.IO) {
|
suspend fun getEtymology(query: String, language: Language): Result<EtymologyData> = withContext(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
Log.i("DictionaryService", "Getting etymology for word: $query in language: ${language.name}")
|
Log.i("DictionaryService", "Getting etymology for word: $query in language: ${language.englishName}")
|
||||||
|
|
||||||
val template = EtymologyRequest(
|
val template = EtymologyRequest(
|
||||||
word = query,
|
word = query,
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package eu.gaudian.translator.view.composable
|
package eu.gaudian.translator.view.composable
|
||||||
|
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.animation.core.Spring
|
import androidx.compose.animation.core.Spring
|
||||||
import androidx.compose.animation.core.animateFloatAsState
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
import androidx.compose.animation.core.spring
|
import androidx.compose.animation.core.spring
|
||||||
@@ -55,7 +56,8 @@ data class FabMenuItem(
|
|||||||
fun AppFabMenu(
|
fun AppFabMenu(
|
||||||
items: List<FabMenuItem>,
|
items: List<FabMenuItem>,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
title: String? = null
|
title: String? = null,
|
||||||
|
showFabText: Boolean = true
|
||||||
) {
|
) {
|
||||||
var isMenuExpanded by remember { mutableStateOf(false) }
|
var isMenuExpanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
@@ -111,14 +113,16 @@ fun AppFabMenu(
|
|||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
modifier = Modifier.padding(horizontal = 16.dp)
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
.animateContentSize()
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = AppIcons.Add,
|
imageVector = AppIcons.Add,
|
||||||
contentDescription = stringResource(R.string.cd_toggle_menu),
|
contentDescription = stringResource(R.string.cd_toggle_menu),
|
||||||
modifier = Modifier.rotate(iconRotationAngle)
|
modifier = Modifier.rotate(iconRotationAngle)
|
||||||
)
|
)
|
||||||
if (title != null) {
|
if (title != null && showFabText) {
|
||||||
Text(
|
Text(
|
||||||
text = title,
|
text = title,
|
||||||
style = MaterialTheme.typography.labelLarge
|
style = MaterialTheme.typography.labelLarge
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import eu.gaudian.translator.viewmodel.VocabularyViewModel
|
|||||||
@Composable
|
@Composable
|
||||||
fun VocabularyMenu(
|
fun VocabularyMenu(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
showFabText : Boolean = true
|
||||||
) {
|
) {
|
||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
@@ -48,7 +49,7 @@ fun VocabularyMenu(
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
AppFabMenu(items = menuItems, modifier = modifier, title = stringResource(R.string.label_add_vocabulary))
|
AppFabMenu(items = menuItems, modifier = modifier, title = stringResource(R.string.label_add_vocabulary), showFabText = showFabText)
|
||||||
|
|
||||||
if (showAddVocabularyDialog) {
|
if (showAddVocabularyDialog) {
|
||||||
AddVocabularyDialog(
|
AddVocabularyDialog(
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ fun DashboardContent(
|
|||||||
onNavigateToCategoryDetail: (Int) -> Unit,
|
onNavigateToCategoryDetail: (Int) -> Unit,
|
||||||
onNavigateToCategoryList: () -> Unit,
|
onNavigateToCategoryList: () -> Unit,
|
||||||
onShowWordPairExerciseDialog: () -> Unit,
|
onShowWordPairExerciseDialog: () -> Unit,
|
||||||
|
onScroll: (Boolean) -> Unit = {},
|
||||||
) {
|
) {
|
||||||
val activity = LocalContext.current.findActivity()
|
val activity = LocalContext.current.findActivity()
|
||||||
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
val languageViewModel: LanguageViewModel = hiltViewModel(viewModelStoreOwner = activity)
|
||||||
@@ -167,6 +168,11 @@ fun DashboardContent(
|
|||||||
lazyListState.firstVisibleItemScrollOffset
|
lazyListState.firstVisibleItemScrollOffset
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Detect scroll and notify parent
|
||||||
|
LaunchedEffect(lazyListState.isScrollInProgress) {
|
||||||
|
onScroll(lazyListState.isScrollInProgress)
|
||||||
|
}
|
||||||
DisposableEffect(Unit) {
|
DisposableEffect(Unit) {
|
||||||
onDispose {
|
onDispose {
|
||||||
settingsViewModel.saveDashboardScrollState(
|
settingsViewModel.saveDashboardScrollState(
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ package eu.gaudian.translator.view.vocabulary
|
|||||||
|
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.LocalActivity
|
import androidx.activity.compose.LocalActivity
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
import androidx.compose.animation.core.animateDpAsState
|
import androidx.compose.animation.core.animateDpAsState
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
@@ -112,6 +113,9 @@ fun MainVocabularyScreen(
|
|||||||
var wpTrainingMode by remember { mutableStateOf(false) }
|
var wpTrainingMode by remember { mutableStateOf(false) }
|
||||||
var wpDueTodayOnly by remember { mutableStateOf(false) }
|
var wpDueTodayOnly by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
var isScrolling by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
|
||||||
if (showCustomExerciseDialog) {
|
if (showCustomExerciseDialog) {
|
||||||
StartExerciseDialog(
|
StartExerciseDialog(
|
||||||
onDismiss = { showCustomExerciseDialog = false },
|
onDismiss = { showCustomExerciseDialog = false },
|
||||||
@@ -293,6 +297,8 @@ fun MainVocabularyScreen(
|
|||||||
VocabularyTab.entries.find { it.route == currentRoute } ?: VocabularyTab.Dashboard
|
VocabularyTab.entries.find { it.route == currentRoute } ?: VocabularyTab.Dashboard
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val showFabText = selectedTab == VocabularyTab.Dashboard && !isScrolling
|
||||||
|
|
||||||
val repoEmpty =
|
val repoEmpty =
|
||||||
vocabularyViewModel.vocabularyItems.collectAsState(initial = emptyList()).value.isEmpty()
|
vocabularyViewModel.vocabularyItems.collectAsState(initial = emptyList()).value.isEmpty()
|
||||||
|
|
||||||
@@ -333,10 +339,11 @@ fun MainVocabularyScreen(
|
|||||||
onNavigateToCategoryList = {
|
onNavigateToCategoryList = {
|
||||||
navController.navigate("category_list_screen")
|
navController.navigate("category_list_screen")
|
||||||
},
|
},
|
||||||
onShowWordPairExerciseDialog = { showWordPairExerciseDialog = true }
|
onShowWordPairExerciseDialog = { showWordPairExerciseDialog = true },
|
||||||
)
|
onScroll = { isScrolling = it }
|
||||||
}
|
)
|
||||||
composable(VocabularyTab.Statistics.route) {
|
}
|
||||||
|
composable(VocabularyTab.Statistics.route) {
|
||||||
StatisticsContent(navController = navController)
|
StatisticsContent(navController = navController)
|
||||||
}
|
}
|
||||||
composable("category_detail/{categoryId}") { backStackEntry ->
|
composable("category_detail/{categoryId}") { backStackEntry ->
|
||||||
@@ -390,7 +397,7 @@ fun MainVocabularyScreen(
|
|||||||
.padding(16.dp),
|
.padding(16.dp),
|
||||||
horizontalAlignment = Alignment.End
|
horizontalAlignment = Alignment.End
|
||||||
) {
|
) {
|
||||||
VocabularyMenu(modifier = Modifier.onSizeChanged { menuHeightPx = it.height })
|
VocabularyMenu(modifier = Modifier.onSizeChanged { menuHeightPx = it.height }, showFabText = showFabText)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Place the FAB separately and animate its bottom padding based on the menu height
|
// Place the FAB separately and animate its bottom padding based on the menu height
|
||||||
@@ -405,16 +412,19 @@ fun MainVocabularyScreen(
|
|||||||
Row(
|
Row(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
modifier = Modifier.padding(horizontal = 16.dp)
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
.animateContentSize()
|
||||||
) {
|
) {
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = AppIcons.Quiz,
|
imageVector = AppIcons.Quiz,
|
||||||
contentDescription = null
|
contentDescription = null
|
||||||
)
|
)
|
||||||
|
if(showFabText) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.label_start_exercise),
|
text = stringResource(R.string.label_start_exercise),
|
||||||
style = MaterialTheme.typography.labelLarge
|
style = MaterialTheme.typography.labelLarge
|
||||||
)
|
)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,11 +10,14 @@ import androidx.compose.foundation.layout.IntrinsicSize
|
|||||||
import androidx.compose.foundation.layout.Row
|
import androidx.compose.foundation.layout.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
|
import androidx.compose.foundation.layout.wrapContentHeight
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
@@ -23,6 +26,7 @@ import androidx.compose.material3.CircularProgressIndicator
|
|||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
|
import androidx.compose.material3.LinearProgressIndicator
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.ModalBottomSheet
|
import androidx.compose.material3.ModalBottomSheet
|
||||||
import androidx.compose.material3.OutlinedCard
|
import androidx.compose.material3.OutlinedCard
|
||||||
@@ -41,7 +45,9 @@ 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.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.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@@ -52,6 +58,7 @@ import androidx.compose.ui.text.style.TextAlign
|
|||||||
import androidx.compose.ui.text.withStyle
|
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.zIndex
|
||||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||||
import androidx.navigation.NavController
|
import androidx.navigation.NavController
|
||||||
import eu.gaudian.translator.R
|
import eu.gaudian.translator.R
|
||||||
@@ -294,21 +301,30 @@ fun VocabularyCard(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
Row(
|
Box(
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
modifier = Modifier
|
||||||
modifier = Modifier.padding(horizontal = 4.dp)
|
.fillMaxWidth()
|
||||||
|
.height(0.dp) // Take 0 structural space
|
||||||
|
.wrapContentHeight(unbounded = true) // Allow children to render at their full size
|
||||||
|
.zIndex(1f), // Ensure it draws over the bottom CardFace
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
HorizontalDivider(
|
Row(
|
||||||
modifier = Modifier.weight(1f),
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.3f)
|
modifier = Modifier.fillMaxWidth()
|
||||||
)
|
) {
|
||||||
if (!exerciseMode && !isFlipped) {
|
HorizontalDivider(
|
||||||
IconButton(onClick = { showActionPanel = true }) {
|
modifier = Modifier.weight(1f),
|
||||||
Icon(
|
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.3f)
|
||||||
imageVector = AppIcons.MoreVert,
|
)
|
||||||
contentDescription = stringResource(R.string.more),
|
if (!exerciseMode && !isFlipped) {
|
||||||
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
IconButton(onClick = { showActionPanel = true }) {
|
||||||
)
|
Icon(
|
||||||
|
imageVector = AppIcons.MoreVert,
|
||||||
|
contentDescription = stringResource(R.string.more),
|
||||||
|
tint = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -442,26 +458,50 @@ private fun FrequencyPill(zipfFrequency: Float?) {
|
|||||||
if (zipfFrequency == null) return
|
if (zipfFrequency == null) return
|
||||||
|
|
||||||
val semanticColors = MaterialTheme.semanticColors
|
val semanticColors = MaterialTheme.semanticColors
|
||||||
|
|
||||||
|
// Determine the label and color based on the Zipf value
|
||||||
val (label, color) = when {
|
val (label, color) = when {
|
||||||
zipfFrequency <= 3f -> Pair(stringResource(R.string.text_rare), semanticColors.wrongContainer)
|
zipfFrequency <= 2.0f -> Pair(stringResource(R.string.text_rare), semanticColors.wrongContainer)
|
||||||
zipfFrequency <= 4f -> Pair(stringResource(R.string.text_infrequent), semanticColors.stageGradient2)
|
zipfFrequency <= 3.0f -> Pair(stringResource(R.string.text_infrequent), semanticColors.stageGradient2)
|
||||||
zipfFrequency <= 4.5f -> Pair(stringResource(R.string.text_uncommon), semanticColors.stageGradient3)
|
zipfFrequency <= 4.0f -> Pair(stringResource(R.string.text_uncommon), semanticColors.stageGradient3)
|
||||||
zipfFrequency <= 5.5f -> Pair(stringResource(R.string.text_common), semanticColors.stageGradient4)
|
zipfFrequency <= 5.5f -> Pair(stringResource(R.string.text_common), semanticColors.stageGradient4)
|
||||||
zipfFrequency <= 6.5f -> Pair(stringResource(R.string.text_frequent), semanticColors.stageGradient5)
|
zipfFrequency <= 6.0f -> Pair(stringResource(R.string.text_frequent), semanticColors.stageGradient5)
|
||||||
zipfFrequency <= 7.5f -> Pair(stringResource(R.string.text_very_frequent), semanticColors.stageGradient6)
|
|
||||||
else -> Pair(stringResource(R.string.text_very_frequent), semanticColors.successContainer)
|
else -> Pair(stringResource(R.string.text_very_frequent), semanticColors.successContainer)
|
||||||
}
|
}
|
||||||
|
|
||||||
Surface(
|
// Normalize progress: Zipf 1.0 to 8.0 mapped to 0f to 1f
|
||||||
shape = RoundedCornerShape(16.dp),
|
val progress = ((zipfFrequency - 1f) / (8f - 1f)).coerceIn(0f, 1f)
|
||||||
color = color.copy(alpha = 0.5f),
|
|
||||||
modifier = Modifier.padding(horizontal = 4.dp)
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.padding(horizontal = 4.dp)
|
||||||
|
.width(80.dp),
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
Text(
|
Surface(
|
||||||
text = label,
|
shape = RoundedCornerShape(16.dp),
|
||||||
style = MaterialTheme.typography.labelMedium,
|
color = color.copy(alpha = 0.2f),
|
||||||
color = MaterialTheme.colorScheme.onSurface,
|
) {
|
||||||
modifier = Modifier.padding(horizontal = 8.dp, vertical = 2.dp)
|
Text(
|
||||||
|
text = label,
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
modifier = Modifier.padding(horizontal = 8.dp, vertical = 2.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
|
||||||
|
// The "Slider" representation
|
||||||
|
LinearProgressIndicator(
|
||||||
|
progress = { progress },
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(4.dp)
|
||||||
|
.clip(CircleShape),
|
||||||
|
color = color,
|
||||||
|
trackColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.1f),
|
||||||
|
strokeCap = StrokeCap.Round
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -986,15 +986,15 @@ class VocabularyViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getExampleForItem(itemId: Int, isFirstWord: Boolean, languageFirst: Language?, languageSecond: Language?): Pair<String, String>? {
|
suspend fun getExampleForItem(itemId: Int, isFirstWord: Boolean, languageFirst: Language?, languageSecond: Language?): Pair<String, String>? {
|
||||||
Log.d(TAG, "Fetching example for item ID $itemId")
|
|
||||||
|
|
||||||
val item = getVocabularyItemById(itemId) ?: return null
|
val item = getVocabularyItemById(itemId) ?: return null
|
||||||
val word = if (isFirstWord) item.wordFirst else item.wordSecond
|
|
||||||
val wordTranslation = if (!isFirstWord) item.wordFirst else item.wordSecond
|
|
||||||
|
|
||||||
if (languageFirst == null || languageSecond == null) return null
|
if (languageFirst == null || languageSecond == null) return null
|
||||||
|
|
||||||
return dictionaryService.getExampleSentence(word, wordTranslation, languageFirst, languageSecond).getOrNull()
|
return if (isFirstWord) {
|
||||||
|
dictionaryService.getExampleSentence(item.wordFirst , item.wordSecond, languageFirst, languageSecond).getOrNull()
|
||||||
|
} else {
|
||||||
|
dictionaryService.getExampleSentence(item.wordSecond, item.wordFirst , languageSecond, languageFirst).getOrNull()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun fetchAndUpdateZipfFrequency(item: VocabularyItem): VocabularyItem {
|
suspend fun fetchAndUpdateZipfFrequency(item: VocabularyItem): VocabularyItem {
|
||||||
|
|||||||
Reference in New Issue
Block a user