Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions app/src/main/java/be/scri/App.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ import be.scri.ui.common.appcomponents.HintDialog
import be.scri.ui.common.bottombar.BottomBarScreen
import be.scri.ui.common.bottombar.ScribeBottomBar
import be.scri.ui.screens.ConjugateScreen
import be.scri.ui.screens.ConjugationSelectionScreen
import be.scri.ui.screens.DefaultCurrencySymbolScreen
import be.scri.ui.screens.InstallationScreen
import be.scri.ui.screens.LanguageSettingsScreen
Expand Down Expand Up @@ -197,6 +198,11 @@ fun ScribeApp(
onNavigateToDownloadData = {
navController.navigate("conjugate_download_data")
},
onNavigateToConjugationSelection = { verb, languageAlias ->
navController.navigate(
"${Screen.ConjugationSelection.route}/$verb/$languageAlias",
)
},
)
}
HandleBackPress(pagerState, coroutineScope)
Expand Down Expand Up @@ -293,6 +299,17 @@ fun ScribeApp(
)
}

composable("${Screen.ConjugationSelection.route}/{verb}/{languageAlias}") { backStackEntry ->
val verb = backStackEntry.arguments?.getString("verb") ?: ""
val languageAlias = backStackEntry.arguments?.getString("languageAlias") ?: ""
ConjugationSelectionScreen(
verb = verb,
languageAlias = languageAlias,
onBackNavigation = { navController.popBackStack() },
modifier = Modifier.padding(innerPadding),
)
}

composable("${Screen.LanguageSettings.route}/{languageName}") {
val language = it.arguments?.getString("languageName")
if (language != null) {
Expand Down
5 changes: 5 additions & 0 deletions app/src/main/java/be/scri/navigation/Screen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -48,4 +48,9 @@ sealed class Screen(
* Screen containing the third party codes used in the application.
*/
data object ThirdParty : Screen("third_party_screen")

/**
* Screen displaying the conjugation selection details for a specific verb.
*/
data object ConjugationSelection : Screen("conjugation_selection_screen")
}
155 changes: 97 additions & 58 deletions app/src/main/java/be/scri/ui/screens/ConjugateScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

package be.scri.ui.screens

import android.widget.Toast
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.border
Expand Down Expand Up @@ -36,7 +35,6 @@ import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
Expand All @@ -53,14 +51,14 @@ import be.scri.ui.common.ScribeBaseScreen
fun ConjugateScreen(
onNavigateToDownloadData: () -> Unit,
modifier: Modifier = Modifier,
onNavigateToConjugationSelection: (String, String) -> Unit = { _, _ -> },
viewModel: ConjugateViewModel = viewModel(),
) {
val context = LocalContext.current
val localConfiguration = LocalConfiguration.current
val scrollState = rememberScrollState()

val searchQuery by viewModel.searchQuery.collectAsState()
val searchResults by viewModel.searchResults.collectAsState()
val displayResults by viewModel.displayResults.collectAsState()
val recentlyConjugated by viewModel.recentlyConjugated.collectAsState()

val dynamicSpacing = localConfiguration.screenHeightDp.dp * 0.1f
Expand Down Expand Up @@ -182,8 +180,10 @@ fun ConjugateScreen(
.width(21.dp)
.height(18.dp)
.clickable {
if (searchResults.isNotEmpty()) {
viewModel.onVerbSelected(searchResults.first())
val first = displayResults.firstOrNull { !it.isDummy }
if (first != null) {
viewModel.onVerbSelected(first)
onNavigateToConjugationSelection(first.verb, first.languageAlias)
}
},
)
Expand All @@ -192,7 +192,7 @@ fun ConjugateScreen(
Spacer(modifier = Modifier.height(Dimensions.PaddingMedium))

if (searchQuery.isNotEmpty()) {
// Search suggestion container
// Search suggestion container — dummy rows until DB is populated (#570)
Card(
modifier =
Modifier
Expand All @@ -211,48 +211,41 @@ fun ConjugateScreen(
.fillMaxWidth()
.padding(Dimensions.PaddingSmall),
) {
if (searchResults.isEmpty()) {
Text(
text = "No verbs found",
modifier = Modifier.padding(Dimensions.PaddingMedium),
color = MaterialTheme.colorScheme.onSurface.copy(alpha = Alpha.MEDIUM),
style = MaterialTheme.typography.bodyMedium,
)
} else {
searchResults.forEachIndexed { index, result ->
Row(
displayResults.forEachIndexed { index, result ->
Row(
modifier =
Modifier
.fillMaxWidth()
.clickable {
viewModel.onVerbSelected(result)
onNavigateToConjugationSelection(result.verb, result.languageAlias)
}.padding(Dimensions.PaddingMedium),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = "${result.verb} (${getLanguageDisplayName(result.languageAlias)})",
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.labelMedium,
)
Image(
painter = painterResource(id = R.drawable.right_arrow),
contentDescription = "Right Arrow",
modifier =
Modifier
.size(Dimensions.IconSize)
.alpha(Alpha.HIGH),
)
}
if (index < displayResults.lastIndex) {
Spacer(
modifier =
Modifier
.fillMaxWidth()
.clickable { viewModel.onVerbSelected(result) }
.padding(Dimensions.PaddingMedium),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = "${result.verb} (${getLanguageDisplayName(result.languageAlias)})",
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.labelMedium,
)
Image(
painter = painterResource(id = R.drawable.right_arrow),
contentDescription = "Right Arrow",
modifier =
Modifier
.size(Dimensions.IconSize)
.alpha(Alpha.HIGH),
)
}
if (index < searchResults.lastIndex) {
Spacer(
modifier =
Modifier
.fillMaxWidth()
.height(1.dp)
.background(MaterialTheme.colorScheme.onSurface.copy(alpha = 0.1f)),
)
}
.height(1.dp)
.background(MaterialTheme.colorScheme.onSurface.copy(alpha = 0.1f)),
)
}
}
}
Expand Down Expand Up @@ -322,7 +315,60 @@ fun ConjugateScreen(
}
}

// Header 3: Recently conjugated
// Direct link to ConjugationSelectionScreen (issue #567).
// Andrew requested a link on the Conjugate tab while search-based
// navigation (#570) is not yet wired up.
Text(
text = stringResource(R.string.i18n_app_conjugate_choose_conjugation_title),
color = MaterialTheme.colorScheme.onSurface,
fontWeight = FontWeight.Bold,
style = MaterialTheme.typography.headlineMedium,
modifier =
Modifier
.padding(
start = 4.dp,
top = Dimensions.PaddingLarge,
bottom = Dimensions.PaddingSmall,
).align(Alignment.Start),
)
Card(
modifier =
Modifier
.fillMaxWidth()
.padding(bottom = Dimensions.PaddingSmall)
.clickable { onNavigateToConjugationSelection("verb", "DE") },
shape = RoundedCornerShape(dimensionResource(id = R.dimen.rounded_corner_radius_standard)),
colors =
CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface,
),
) {
Row(
modifier =
Modifier
.padding(Dimensions.PaddingMedium)
.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = stringResource(R.string.i18n_app_conjugate_choose_conjugation_select_tense),
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onSurface,
style = MaterialTheme.typography.labelMedium,
)
Image(
painter = painterResource(R.drawable.right_arrow),
contentDescription = "Right Arrow",
modifier =
Modifier
.size(Dimensions.IconSize)
.alpha(Alpha.HIGH),
)
}
}

// Header 3: Recently conjugated — only shown when real items exist.
if (recentlyConjugated.isNotEmpty()) {
Row(
modifier =
Expand Down Expand Up @@ -365,23 +411,16 @@ fun ConjugateScreen(
containerColor = MaterialTheme.colorScheme.surface,
),
) {
Column(
modifier = Modifier.fillMaxWidth(),
) {
Column(modifier = Modifier.fillMaxWidth()) {
recentlyConjugated.forEachIndexed { index, item ->
Row(
modifier =
Modifier
.fillMaxWidth()
.padding(Dimensions.PaddingMedium)
.clickable {
Toast
.makeText(
context,
"Conjugating ${item.verb} (${getLanguageDisplayName(item.languageAlias)})...",
Toast.LENGTH_SHORT,
).show()
},
viewModel.onVerbSelected(item)
onNavigateToConjugationSelection(item.verb, item.languageAlias)
}.padding(Dimensions.PaddingMedium),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically,
) {
Expand Down
32 changes: 30 additions & 2 deletions app/src/main/java/be/scri/ui/screens/ConjugateViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ import be.scri.helpers.data.columnExists
import be.scri.helpers.data.tableExists
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

Expand All @@ -22,6 +25,8 @@ import kotlinx.coroutines.withContext
data class ConjugateSearchResult(
val verb: String,
val languageAlias: String,
/** True when this entry is a static placeholder shown before DB data is available. */
val isDummy: Boolean = false,
)

/**
Expand All @@ -41,6 +46,28 @@ class ConjugateViewModel(
private val _recentlyConjugated = MutableStateFlow<List<ConjugateSearchResult>>(emptyList())
val recentlyConjugated = _recentlyConjugated.asStateFlow()

/**
* Results shown in the search dropdown.
* - Blank query → empty
* - Real DB results → show them
* - No DB yet → dummy rows using the typed query as label
*/
val displayResults =
combine(_searchQuery, _searchResults) { query, results ->
when {
query.isBlank() -> emptyList()
results.isNotEmpty() -> results
else ->
listOf("DE", "ES", "FR", "IT", "PT", "RU", "SV", "EN", "DE").map { alias ->
ConjugateSearchResult(verb = query, languageAlias = alias, isDummy = true)
}
}
}.stateIn(
scope = viewModelScope,
started = SharingStarted.WhileSubscribed(5_000),
initialValue = emptyList(),
)

init {
loadRecentlyConjugated()
}
Expand Down Expand Up @@ -114,10 +141,11 @@ class ConjugateViewModel(
}

/**
* Triggers when a verb search result or recently conjugated item is clicked.
* Triggers when a verb is selected. Always persists to recently conjugated
* (stripping isDummy) so the section shows real user history.
*/
fun onVerbSelected(result: ConjugateSearchResult) {
addToRecentlyConjugated(result)
addToRecentlyConjugated(result.copy(isDummy = false))
clearSearchQuery()
}

Expand Down
Loading
Loading