RNDAMP-8921: use new bottom bar

This commit is contained in:
2026-08-30 13:55:35 +03:00
parent ed11b3c967
commit 63116b4a08
3 changed files with 66 additions and 71 deletions
@@ -5,7 +5,6 @@ import androiddev.feature.updater.updater_client.generated.resources.updater_btn
import androiddev.feature.updater.updater_client.generated.resources.updater_btn_download import androiddev.feature.updater.updater_client.generated.resources.updater_btn_download
import androiddev.feature.updater.updater_client.generated.resources.updater_btn_grant import androiddev.feature.updater.updater_client.generated.resources.updater_btn_grant
import androiddev.feature.updater.updater_client.generated.resources.updater_btn_install import androiddev.feature.updater.updater_client.generated.resources.updater_btn_install
import androiddev.feature.updater.updater_client.generated.resources.updater_btn_retry
import androiddev.feature.updater.updater_client.generated.resources.updater_changelog_empty import androiddev.feature.updater.updater_client.generated.resources.updater_changelog_empty
import androiddev.feature.updater.updater_client.generated.resources.updater_changelog_title import androiddev.feature.updater.updater_client.generated.resources.updater_changelog_title
import androiddev.feature.updater.updater_client.generated.resources.updater_desc_permission import androiddev.feature.updater.updater_client.generated.resources.updater_desc_permission
@@ -13,7 +12,6 @@ import androiddev.feature.updater.updater_client.generated.resources.updater_dow
import androiddev.feature.updater.updater_client.generated.resources.updater_title_available import androiddev.feature.updater.updater_client.generated.resources.updater_title_available
import androiddev.feature.updater.updater_client.generated.resources.updater_title_completed import androiddev.feature.updater.updater_client.generated.resources.updater_title_completed
import androiddev.feature.updater.updater_client.generated.resources.updater_title_downloading import androiddev.feature.updater.updater_client.generated.resources.updater_title_downloading
import androiddev.feature.updater.updater_client.generated.resources.updater_title_error
import androiddev.feature.updater.updater_client.generated.resources.updater_title_not_found import androiddev.feature.updater.updater_client.generated.resources.updater_title_not_found
import androiddev.feature.updater.updater_client.generated.resources.updater_title_permission import androiddev.feature.updater.updater_client.generated.resources.updater_title_permission
import androiddev.feature.updater.updater_client.generated.resources.updater_title_ready import androiddev.feature.updater.updater_client.generated.resources.updater_title_ready
@@ -43,16 +41,14 @@ import com.arkivanov.decompose.extensions.compose.subscribeAsState
import org.jetbrains.compose.resources.StringResource import org.jetbrains.compose.resources.StringResource
import org.jetbrains.compose.resources.stringResource import org.jetbrains.compose.resources.stringResource
import ru.shadowsparky.ui.components.AlertContent import ru.shadowsparky.ui.components.AlertContent
import ru.shadowsparky.ui.components.ErrorContent
import ru.shadowsparky.ui.components.LazyColumn import ru.shadowsparky.ui.components.LazyColumn
import ru.shadowsparky.ui.components.Loading import ru.shadowsparky.ui.components.Loading
import ru.shadowsparky.updater.domain.UpdateState import ru.shadowsparky.updater.domain.UpdateState
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun UpdateDialog( fun UpdateDialog(component: UpdateComponent, modifier: Modifier = Modifier) {
component: UpdateComponent,
modifier: Modifier = Modifier
) {
val state by component.state.subscribeAsState() val state by component.state.subscribeAsState()
val canDismiss = remember(state) { !component.isForceUpdate } val canDismiss = remember(state) { !component.isForceUpdate }
val isVisible = remember(state) { state !is UpdateState.Idle } val isVisible = remember(state) { state !is UpdateState.Idle }
@@ -90,8 +86,7 @@ fun UpdateDialog(
is UpdateState.Error -> UpdateErrorContent( is UpdateState.Error -> UpdateErrorContent(
message = currentState.message, message = currentState.message,
onRetryClick = { component.checkUpdates() }, onRetryClick = { component.checkUpdates() }
focusRequester = focusRequester
) )
is UpdateState.UpToDate -> { is UpdateState.UpToDate -> {
@@ -158,6 +153,9 @@ private fun UpdateAvailableContent(
style = MaterialTheme.typography.bodyMedium style = MaterialTheme.typography.bodyMedium
) )
} }
item {
Spacer(modifier = Modifier.height(8.dp))
}
item { item {
Button( Button(
onClick = onDownloadClick, onClick = onDownloadClick,
@@ -290,76 +288,51 @@ private fun UpdateFinishedContent(
private fun UpdateErrorContent( private fun UpdateErrorContent(
message: String, message: String,
onRetryClick: () -> Unit, onRetryClick: () -> Unit,
focusRequester: FocusRequester,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
Column( ErrorContent(
modifier = modifier.fillMaxWidth(), message,
horizontalAlignment = Alignment.CenterHorizontally, modifier,
verticalArrangement = Arrangement.spacedBy(16.dp) onClick = onRetryClick,
) { buttonModifier = Modifier.fillMaxWidth()
Text( )
text = stringResource(Res.string.updater_title_error),
style = MaterialTheme.typography.titleLarge,
color = MaterialTheme.colorScheme.error
)
Text(text = message, style = MaterialTheme.typography.bodyMedium)
Button(
onClick = onRetryClick,
modifier = Modifier.focusRequester(focusRequester).fillMaxWidth()
) {
Text(
stringResource(Res.string.updater_btn_retry),
style = MaterialTheme.typography.labelLarge
)
}
}
} }
@Preview @Preview
@Composable @Composable
private fun UpdateAvailablePreview() { private fun UpdateAvailablePreview() {
MaterialTheme { UpdateAvailableContent(
UpdateAvailableContent( versionName = "v1.4.0",
versionName = "v1.4.0", changeLog = "- Добавили темную тему\n- Исправили падение на главном экране\n- Повысили стабильность",
changeLog = "- Добавили темную тему\n- Исправили падение на главном экране\n- Повысили стабильность", onDownloadClick = {},
onDownloadClick = {}, focusRequester = FocusRequester()
focusRequester = FocusRequester() )
)
}
} }
@Preview @Preview
@Composable @Composable
private fun UpdateDownloadingPreview() { private fun UpdateDownloadingPreview() {
MaterialTheme { UpdateDownloadingContent(
UpdateDownloadingContent( progress = 0.45f,
progress = 0.45f, downloadedBytes = 24_117_248L, // ~23 MB
downloadedBytes = 24_117_248L, // ~23 MB totalBytes = 52_428_800L // 50 MB
totalBytes = 52_428_800L // 50 MB )
)
}
} }
@Preview @Preview
@Composable @Composable
private fun UpdatePermissionPreview() { private fun UpdatePermissionPreview() {
MaterialTheme { UpdatePermissionContent(
UpdatePermissionContent( onGrantClick = {},
onGrantClick = {}, focusRequester = FocusRequester()
focusRequester = FocusRequester() )
)
}
} }
@Preview @Preview
@Composable @Composable
private fun UpdateErrorPreview() { private fun UpdateErrorPreview() {
MaterialTheme { UpdateErrorContent(
UpdateErrorContent( message = "Http status 503: Service Unavailable",
message = "Http status 503: Service Unavailable", onRetryClick = {}
onRetryClick = {}, )
focusRequester = FocusRequester()
)
}
} }
@@ -3,23 +3,26 @@ package ru.shadowsparky.ui
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.windowInsetsPadding import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.layout.wrapContentWidth
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.IconButtonDefaults
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarDefaults import androidx.compose.material3.NavigationBarDefaults
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.SnackbarHost import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.TopAppBarScrollBehavior import androidx.compose.material3.TopAppBarScrollBehavior
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
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.Brush import androidx.compose.ui.graphics.Brush
@@ -62,26 +65,41 @@ private fun <T : Route> BottomBar(
) { ) {
navigationEntries?.let { entries -> navigationEntries?.let { entries ->
Box( Box(
modifier = Modifier modifier = Modifier.fillMaxWidth()
.fillMaxWidth()
.background( .background(
Brush.verticalGradient( Brush.verticalGradient(
listOf(Color.Transparent, MaterialTheme.colorScheme.surfaceContainer) listOf(Color.Transparent, MaterialTheme.colorScheme.surfaceContainer)
) )
) )
.padding(8.dp) .padding(8.dp)
.windowInsetsPadding(WindowInsets.navigationBars) .windowInsetsPadding(WindowInsets.navigationBars),
contentAlignment = Alignment.Center
) { ) {
NavigationBar( Row(
containerColor = navBarContentColor(), modifier = Modifier.clip(RoundedCornerShape(24.dp))
modifier = Modifier.clip(RoundedCornerShape(24.dp)).blurEffect() .wrapContentWidth()
.background(navBarContentColor())
.blurEffect()
.padding(8.dp)
) { ) {
entries.forEach { entry -> entries.forEach { entry ->
NavigationBarItem( val containerColor = if (currentRoute == entry.route) {
selected = currentRoute == entry.route, MaterialTheme.colorScheme.secondaryContainer
} else {
Color.Unspecified
}
val contentColor = if (currentRoute == entry.route) {
MaterialTheme.colorScheme.onSecondaryContainer
} else {
Color.Unspecified
}
IconButton(
onClick = { onSelectNavigation?.invoke(entry.route) }, onClick = { onSelectNavigation?.invoke(entry.route) },
icon = { Icon(entry.painter, contentDescription = entry.title) } modifier = Modifier.padding(4.dp),
) colors = IconButtonDefaults.iconButtonColors(containerColor, contentColor)
) {
Icon(entry.painter, contentDescription = entry.title)
}
} }
} }
} }
@@ -14,8 +14,11 @@ import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester
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
@@ -29,6 +32,7 @@ fun ErrorContent(
retry: Boolean = true, retry: Boolean = true,
onClick: () -> Unit onClick: () -> Unit
) { ) {
val requester = remember { FocusRequester() }
val buttonTextRes = if (retry) Res.string.retry else Res.string.ok val buttonTextRes = if (retry) Res.string.retry else Res.string.ok
Column( Column(
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
@@ -47,7 +51,7 @@ fun ErrorContent(
textAlign = TextAlign.Center textAlign = TextAlign.Center
) )
Spacer(Modifier.height(8.dp)) Spacer(Modifier.height(8.dp))
Button(modifier = buttonModifier, onClick = { onClick() }) { Button(modifier = buttonModifier.focusRequester(requester), onClick = { onClick() }) {
Text( Text(
text = stringResource(buttonTextRes), text = stringResource(buttonTextRes),
style = MaterialTheme.typography.bodyMedium style = MaterialTheme.typography.bodyMedium