feat: language fragment to jetpack compose

- added orders to the test rule to avoid retries error in tests.
- renamed content lambda to "navigationIcon".
- removed redundant "Experimental api" from kiwixSearchView.
- added appropriate text size for kiwixSearchView.
This commit is contained in:
jaskaran 2025-03-28 15:32:45 +05:30 committed by MohitMaliFtechiz
parent 90830da5ca
commit 56e0c1c7b1
3 changed files with 86 additions and 55 deletions

View File

@ -27,6 +27,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListState import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@ -47,6 +48,7 @@ import org.kiwix.kiwixmobile.language.viewmodel.LanguageViewModel
import org.kiwix.kiwixmobile.language.viewmodel.State import org.kiwix.kiwixmobile.language.viewmodel.State
import org.kiwix.kiwixmobile.language.viewmodel.State.Content import org.kiwix.kiwixmobile.language.viewmodel.State.Content
@OptIn(ExperimentalMaterial3Api::class)
@SuppressLint("ComposableLambdaParameterNaming") @SuppressLint("ComposableLambdaParameterNaming")
@Composable @Composable
fun LanguageScreen( fun LanguageScreen(
@ -68,13 +70,13 @@ fun LanguageScreen(
navigationIcon = navigationIcon, navigationIcon = navigationIcon,
actionMenuItems = actionMenuItemList, actionMenuItems = actionMenuItemList,
searchBar = if (isSearchActive) { searchBar = if (isSearchActive) {
{ modifier -> {
KiwixSearchView( KiwixSearchView(
modifier = modifier,
value = searchText, value = searchText,
testTag = SEARCH_FIELD_TESTING_TAG, testTag = SEARCH_FIELD_TESTING_TAG,
onValueChange = onAppBarValueChange, onValueChange = onAppBarValueChange,
onClearClick = onClearClick onClearClick = onClearClick,
modifier = Modifier
) )
} }
} else { } else {

View File

@ -42,21 +42,11 @@ fun ContentLoadingProgressBar(
) { ) {
when (progressBarStyle) { when (progressBarStyle) {
ProgressBarStyle.CIRCLE -> { ProgressBarStyle.CIRCLE -> {
if (progress == ZERO) { CircularProgressIndicator(
// Indeterminate mode - will spin continuously modifier = modifier,
CircularProgressIndicator( color = progressBarColor,
modifier = modifier, trackColor = progressBarTrackColor
color = progressBarColor )
)
} else {
// Determinate mode - shows specific progress
CircularProgressIndicator(
modifier = modifier,
progress = { progress.toFloat() / HUNDERED },
color = progressBarColor,
trackColor = progressBarTrackColor
)
}
} }
ProgressBarStyle.HORIZONTAL -> { ProgressBarStyle.HORIZONTAL -> {

View File

@ -19,64 +19,86 @@
package org.kiwix.kiwixmobile.core.ui.components package org.kiwix.kiwixmobile.core.ui.components
import androidx.annotation.StringRes import androidx.annotation.StringRes
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyListState
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow
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.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.rememberVectorPainter
import androidx.compose.ui.platform.testTag import androidx.compose.ui.platform.testTag
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.Companion.SemiBold import androidx.compose.ui.text.style.TextOverflow
import org.kiwix.kiwixmobile.core.downloader.downloadManager.ZERO
import org.kiwix.kiwixmobile.core.ui.models.ActionMenuItem import org.kiwix.kiwixmobile.core.ui.models.ActionMenuItem
import org.kiwix.kiwixmobile.core.ui.models.IconItem import org.kiwix.kiwixmobile.core.ui.models.toPainter
import org.kiwix.kiwixmobile.core.ui.theme.Black import org.kiwix.kiwixmobile.core.ui.theme.Black
import org.kiwix.kiwixmobile.core.ui.theme.KiwixTheme import org.kiwix.kiwixmobile.core.ui.theme.KiwixTheme
import org.kiwix.kiwixmobile.core.ui.theme.MineShaftGray350 import org.kiwix.kiwixmobile.core.ui.theme.MineShaftGray350
import org.kiwix.kiwixmobile.core.ui.theme.White import org.kiwix.kiwixmobile.core.ui.theme.White
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.KIWIX_APP_BAR_HEIGHT
import org.kiwix.kiwixmobile.core.utils.ComposeDimens.SIXTEEN_DP import org.kiwix.kiwixmobile.core.utils.ComposeDimens.SIXTEEN_DP
const val TOOLBAR_TITLE_TESTING_TAG = "toolbarTitle" const val TOOLBAR_TITLE_TESTING_TAG = "toolbarTitle"
@OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun KiwixAppBar( fun KiwixAppBar(
@StringRes titleId: Int, @StringRes titleId: Int,
navigationIcon: @Composable () -> Unit, navigationIcon: @Composable () -> Unit,
actionMenuItems: List<ActionMenuItem> = emptyList(), actionMenuItems: List<ActionMenuItem> = emptyList(),
topAppBarScrollBehavior: TopAppBarScrollBehavior = TopAppBarDefaults.pinnedScrollBehavior(),
// Optional search bar, used in fragments that require it // Optional search bar, used in fragments that require it
searchBar: (@Composable (Modifier) -> Unit)? = null searchBar: (@Composable () -> Unit)? = null
) { ) {
KiwixTheme { KiwixTheme {
Row( TopAppBar(
modifier = Modifier title = { AppBarTitleSection(titleId, searchBar) },
.fillMaxWidth() navigationIcon = navigationIcon,
.height(KIWIX_APP_BAR_HEIGHT) actions = { ActionMenu(actionMenuItems) },
.background(color = Black), scrollBehavior = topAppBarScrollBehavior,
verticalAlignment = Alignment.CenterVertically colors = TopAppBarDefaults.topAppBarColors(
) { containerColor = Black,
navigationIcon() scrolledContainerColor = Black
searchBar?.let { searchBarComposable -> )
searchBarComposable( )
Modifier.weight(1f) }
) }
} ?: run {
// Otherwise, show the title @Suppress("ComposableLambdaParameterNaming")
AppBarTitle(titleId) @Composable
Spacer(Modifier.weight(1f)) private fun AppBarTitleSection(
} @StringRes titleId: Int,
ActionMenu(actionMenuItems) searchBar: (@Composable () -> Unit)? = null
) {
Box(
modifier = Modifier
.fillMaxSize()
.padding(start = SIXTEEN_DP),
contentAlignment = Alignment.CenterStart
) {
searchBar?.let {
it()
} ?: run {
AppBarTitle(titleId)
} }
} }
} }
@ -93,10 +115,11 @@ private fun AppBarTitle(
Text( Text(
text = stringResource(titleId), text = stringResource(titleId),
color = appBarTitleColor, color = appBarTitleColor,
style = MaterialTheme.typography.titleLarge.copy(fontWeight = SemiBold), style = MaterialTheme.typography.titleMedium,
overflow = TextOverflow.Ellipsis,
maxLines = 1,
modifier = Modifier modifier = Modifier
.padding(horizontal = SIXTEEN_DP) .testTag(TOOLBAR_TITLE_TESTING_TAG),
.testTag(TOOLBAR_TITLE_TESTING_TAG)
) )
} }
@ -107,14 +130,10 @@ private fun ActionMenu(actionMenuItems: List<ActionMenuItem>) {
IconButton( IconButton(
enabled = menuItem.isEnabled, enabled = menuItem.isEnabled,
onClick = menuItem.onClick, onClick = menuItem.onClick,
modifier = Modifier modifier = Modifier.testTag(menuItem.testingTag)
.testTag(menuItem.testingTag)
) { ) {
Icon( Icon(
painter = when (val icon = menuItem.icon) { painter = menuItem.icon.toPainter(),
is IconItem.Vector -> rememberVectorPainter(icon.imageVector)
is IconItem.Drawable -> painterResource(icon.drawableRes)
},
contentDescription = stringResource(menuItem.contentDescription), contentDescription = stringResource(menuItem.contentDescription),
tint = if (menuItem.isEnabled) menuItem.iconTint else Color.Gray tint = if (menuItem.isEnabled) menuItem.iconTint else Color.Gray
) )
@ -122,3 +141,23 @@ private fun ActionMenu(actionMenuItems: List<ActionMenuItem>) {
} }
} }
} }
@Composable
fun rememberBottomNavigationVisibility(lazyListState: LazyListState?): Boolean {
var isToolbarVisible by remember { mutableStateOf(true) }
var lastScrollIndex by remember { mutableIntStateOf(ZERO) }
val updatedLazyListState = rememberUpdatedState(lazyListState)
LaunchedEffect(updatedLazyListState) {
updatedLazyListState.value?.let { state ->
snapshotFlow { state.firstVisibleItemIndex }
.collect { newScrollIndex ->
if (newScrollIndex != lastScrollIndex) {
isToolbarVisible = newScrollIndex < lastScrollIndex
lastScrollIndex = newScrollIndex
}
}
}
}
return isToolbarVisible
}