From 9c38da76d2731b77f1088634bf23525425e11412 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=9F=D1=8B=D1=82=D0=BA=D0=BE=D0=B2=20=D0=A0=D0=BE=D0=BC?= =?UTF-8?q?=D0=B0=D0=BD?= Date: Thu, 21 May 2026 01:10:55 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9A=D1=80=D0=B0=D1=81=D0=B8=D0=B2=D1=8B?= =?UTF-8?q?=D0=B9=20UI?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../github/nullptroma/wallenc/ui/WallencUi.kt | 119 ++++++------ .../wallenc/ui/elements/FloatingBackButton.kt | 75 ++++++++ .../elements/FloatingWallencNavigationBar.kt | 180 ++++++++++++++++++ .../ui/elements/NavigationBarMarqueeText.kt | 16 +- .../ui/elements/WallencScreenScaffold.kt | 49 +++++ .../wallenc/ui/navigation/MainNavRoutes.kt | 25 +++ .../wallenc/ui/navigation/NavigationState.kt | 4 + .../wallenc/ui/screens/main/MainScreen.kt | 108 ++++++----- .../main/screens/storage/StorageHomeScreen.kt | 16 +- .../secrets/TextSecretDetailsScreen.kt | 16 +- .../storage/secrets/TextSecretEditScreen.kt | 16 +- .../storage/secrets/TextSecretsScreen.kt | 13 +- .../storage/twofa/TwoFaTokensScreen.kt | 11 +- .../storage/twofa/TwoFaTokensScreenContent.kt | 4 +- .../main/screens/vault/VaultBrowserScreen.kt | 54 +++--- .../ui/screens/shared/TextEditScreen.kt | 22 ++- .../ui/screens/sync/StorageSyncScreen.kt | 35 ++-- ui/src/main/res/values-ru/strings.xml | 1 + ui/src/main/res/values/strings.xml | 6 + 19 files changed, 557 insertions(+), 213 deletions(-) create mode 100644 ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/FloatingBackButton.kt create mode 100644 ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/FloatingWallencNavigationBar.kt create mode 100644 ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/WallencScreenScaffold.kt create mode 100644 ui/src/main/java/com/github/nullptroma/wallenc/ui/navigation/MainNavRoutes.kt diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/WallencUi.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/WallencUi.kt index f075236..aec1aa2 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/WallencUi.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/WallencUi.kt @@ -4,17 +4,15 @@ import android.content.Intent import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.wrapContentHeight import androidx.compose.material.icons.Icons import androidx.compose.material.icons.automirrored.rounded.List import androidx.compose.material.icons.rounded.Menu import androidx.compose.material.icons.rounded.Settings import androidx.compose.material.icons.rounded.Sync import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.NavigationBar -import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.runtime.Composable @@ -22,16 +20,16 @@ import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.navDeepLink +import com.github.nullptroma.wallenc.ui.elements.FloatingWallencNavigationBar import com.github.nullptroma.wallenc.ui.navigation.NavBarItemData import com.github.nullptroma.wallenc.ui.navigation.WallencDeepLinks import com.github.nullptroma.wallenc.ui.navigation.matchesWallencDeepLink -import com.github.nullptroma.wallenc.ui.elements.NavigationBarMarqueeText import com.github.nullptroma.wallenc.ui.navigation.rememberNavigationState import com.github.nullptroma.wallenc.ui.screens.main.MainRoute import com.github.nullptroma.wallenc.ui.screens.main.MainScreen @@ -108,57 +106,52 @@ fun WallencNavRoot( ) } + val navBackStackEntry by navState.navHostController.currentBackStackEntryAsState() + val currentRoute = navBackStackEntry?.destination?.route - Scaffold(bottomBar = { - NavigationBar(modifier = Modifier.wrapContentHeight()) { - val navBackStackEntry by navState.navHostController.currentBackStackEntryAsState() - val currentRoute = navBackStackEntry?.destination?.route - topLevelNavBarItems.forEach { - val routeClassName = it.key - val navBarItemData = it.value - NavigationBarItem( - modifier = Modifier.wrapContentHeight(), - icon = { - if (navBarItemData.icon != null) Icon( - navBarItemData.icon, - contentDescription = stringResource(navBarItemData.iconContentDescriptionResourceId), - ) - }, - label = { - NavigationBarMarqueeText( - text = stringResource(navBarItemData.nameStringResourceId), - ) + Scaffold( + bottomBar = { + Box( + modifier = Modifier + .navigationBarsPadding() + .padding(horizontal = 12.dp) + .padding(top = 4.dp, bottom = 6.dp), + ) { + FloatingWallencNavigationBar( + items = topLevelNavBarItems, + routes = topLevelRoutes, + currentRoute = currentRoute, + onNavigate = { item -> + val route = topLevelRoutes[item.screenRouteClass] + ?: error("Route ${item.screenRouteClass} not found") + if (currentRoute?.startsWith(item.screenRouteClass) != true) { + navState.changeTop(route) + } }, - selected = currentRoute?.startsWith(routeClassName) == true, - onClick = { - val route = topLevelRoutes[navBarItemData.screenRouteClass] - if (route == null) - throw NullPointerException("Route $route not found") - if (currentRoute?.startsWith(routeClassName) != true) navState.changeTop( - route - ) - } ) } - } - }) { innerPaddings -> + }, + ) { innerPaddings -> NavHost( navState.navHostController, - startDestination = topLevelRoutes[MainRoute::class.qualifiedName]!! + startDestination = topLevelRoutes[MainRoute::class.qualifiedName]!!, + modifier = Modifier.padding(innerPaddings), ) { composable( deepLinks = listOf( navDeepLink { uriPattern = WallencDeepLinks.MAIN_URI_PATTERN }, ), enterTransition = { - fadeIn(tween(200)) - }, exitTransition = { - fadeOut(tween(200)) - }) { + fadeIn(tween(200)) + }, + exitTransition = { + fadeOut(tween(200)) + }, + ) { MainScreen( - modifier = Modifier.padding(innerPaddings), + modifier = Modifier, navState = mainNavState, - viewModel = mainViewModel + viewModel = mainViewModel, ) } composable( @@ -166,23 +159,27 @@ fun WallencNavRoot( navDeepLink { uriPattern = WallencDeepLinks.SETTINGS_URI_PATTERN }, ), enterTransition = { - fadeIn(tween(200)) - }, exitTransition = { - fadeOut(tween(200)) - }) { - SettingsScreen(Modifier.padding(innerPaddings), settingsViewModel) + fadeIn(tween(200)) + }, + exitTransition = { + fadeOut(tween(200)) + }, + ) { + SettingsScreen(Modifier, settingsViewModel) } composable( deepLinks = listOf( navDeepLink { uriPattern = WallencDeepLinks.SYNC_URI_PATTERN }, ), enterTransition = { - fadeIn(tween(200)) - }, exitTransition = { - fadeOut(tween(200)) - }) { + fadeIn(tween(200)) + }, + exitTransition = { + fadeOut(tween(200)) + }, + ) { StorageSyncScreen( - modifier = Modifier.padding(innerPaddings), + modifier = Modifier, viewModel = storageSyncViewModel, ) } @@ -191,14 +188,14 @@ fun WallencNavRoot( navDeepLink { uriPattern = WallencDeepLinks.TASKS_URI_PATTERN }, ), enterTransition = { - fadeIn(tween(200)) - }, exitTransition = { - fadeOut(tween(200)) - }) { - TaskPipelineScreen( - modifier = Modifier.padding(innerPaddings) - ) + fadeIn(tween(200)) + }, + exitTransition = { + fadeOut(tween(200)) + }, + ) { + TaskPipelineScreen(modifier = Modifier) } } } -} \ No newline at end of file +} diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/FloatingBackButton.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/FloatingBackButton.kt new file mode 100644 index 0000000..bfcad56 --- /dev/null +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/FloatingBackButton.kt @@ -0,0 +1,75 @@ +package com.github.nullptroma.wallenc.ui.elements + +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.animation.core.tween +import androidx.compose.animation.fadeIn +import androidx.compose.animation.fadeOut +import androidx.compose.animation.slideInVertically +import androidx.compose.animation.slideOutVertically +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.shape.CircleShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.rounded.ArrowBack +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.unit.dp +import com.github.nullptroma.wallenc.ui.R + +private const val BackButtonAnimMillis = 200 + +private val backButtonEnter = fadeIn(tween(BackButtonAnimMillis)) + + slideInVertically( + animationSpec = tween(BackButtonAnimMillis), + initialOffsetY = { fullHeight -> fullHeight / 2 }, + ) + +private val backButtonExit = fadeOut(tween(BackButtonAnimMillis)) + + slideOutVertically( + animationSpec = tween(BackButtonAnimMillis), + targetOffsetY = { fullHeight -> -fullHeight / 2 }, + ) + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun FloatingBackButton( + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + Surface( + onClick = onClick, + modifier = modifier.size(44.dp), + shape = CircleShape, + color = MaterialTheme.colorScheme.surfaceContainerHigh, + shadowElevation = 4.dp, + tonalElevation = 2.dp, + contentColor = MaterialTheme.colorScheme.onSurface, + ) { + Icon( + imageVector = Icons.AutoMirrored.Rounded.ArrowBack, + contentDescription = stringResource(R.string.nav_cd_back), + modifier = Modifier.padding(10.dp), + ) + } +} + +@Composable +fun AnimatedFloatingBackButton( + visible: Boolean, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + AnimatedVisibility( + visible = visible, + modifier = modifier, + enter = backButtonEnter, + exit = backButtonExit, + ) { + FloatingBackButton(onClick = onClick) + } +} diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/FloatingWallencNavigationBar.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/FloatingWallencNavigationBar.kt new file mode 100644 index 0000000..f03ae80 --- /dev/null +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/FloatingWallencNavigationBar.kt @@ -0,0 +1,180 @@ +package com.github.nullptroma.wallenc.ui.elements + +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Surface +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.hapticfeedback.HapticFeedbackType +import androidx.compose.ui.platform.LocalHapticFeedback +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.semantics.contentDescription +import androidx.compose.ui.semantics.role +import androidx.compose.ui.semantics.semantics +import androidx.compose.ui.unit.dp +import com.github.nullptroma.wallenc.ui.navigation.NavBarItemData + +/** Вертикальный зазор между вложенной и корневой плавающими панелями навигации. */ +val WallencNestedNavBarGap = 2.dp + +@Composable +fun FloatingWallencNavigationBar( + items: Map, + routes: Map, + currentRoute: String?, + onNavigate: (NavBarItemData) -> Unit, + modifier: Modifier = Modifier, + compact: Boolean = false, +) { + val haptic = LocalHapticFeedback.current + val barHeight = if (compact) 48.dp else 56.dp + val barShape = if (compact) RoundedCornerShape(22.dp) else RoundedCornerShape(28.dp) + val barHorizontalPadding = if (compact) 4.dp else 6.dp + + val barSurface: @Composable () -> Unit = { + Surface( + modifier = Modifier + .then( + if (compact) { + Modifier + .widthIn(max = 300.dp) + .fillMaxWidth(0.68f) + } else { + Modifier.fillMaxWidth() + }, + ), + shape = barShape, + color = MaterialTheme.colorScheme.surfaceContainerHigh, + shadowElevation = 6.dp, + tonalElevation = 2.dp, + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .height(barHeight) + .padding(horizontal = barHorizontalPadding, vertical = 4.dp), + verticalAlignment = Alignment.CenterVertically, + ) { + items.forEach { (routeClassName, navBarItemData) -> + val iconVector = navBarItemData.icon ?: return@forEach + val selected = currentRoute?.startsWith(routeClassName) == true + val enabled = routes[navBarItemData.screenRouteClass] != null + FloatingNavItem( + modifier = Modifier + .weight(1f) + .fillMaxWidth(), + icon = iconVector, + label = stringResource(navBarItemData.nameStringResourceId), + contentDescription = stringResource(navBarItemData.iconContentDescriptionResourceId), + selected = selected, + enabled = enabled, + compact = compact, + onClick = { + if (!selected && enabled) { + haptic.performHapticFeedback(HapticFeedbackType.TextHandleMove) + onNavigate(navBarItemData) + } + }, + ) + } + } + } + } + + if (compact) { + Box( + modifier = modifier.fillMaxWidth(), + contentAlignment = Alignment.Center, + ) { + barSurface() + } + } else { + Box(modifier = modifier.fillMaxWidth()) { + barSurface() + } + } +} + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +private fun FloatingNavItem( + icon: ImageVector, + label: String, + contentDescription: String, + selected: Boolean, + enabled: Boolean, + compact: Boolean, + onClick: () -> Unit, + modifier: Modifier = Modifier, +) { + val iconSize = if (compact) 22.dp else 24.dp + val itemPaddingH = if (compact) 6.dp else 8.dp + val itemPaddingV = if (compact) 6.dp else 8.dp + val labelStyle = if (compact) { + MaterialTheme.typography.labelSmall + } else { + MaterialTheme.typography.labelMedium + } + val labelVelocity = if (compact) 24.dp else 28.dp + val itemShape = if (compact) RoundedCornerShape(16.dp) else RoundedCornerShape(20.dp) + val containerColor = if (selected) { + MaterialTheme.colorScheme.secondaryContainer + } else { + MaterialTheme.colorScheme.surfaceContainerHigh + } + val contentColor = if (selected) { + MaterialTheme.colorScheme.onSecondaryContainer + } else { + MaterialTheme.colorScheme.onSurfaceVariant + } + Surface( + onClick = onClick, + enabled = enabled, + modifier = modifier + .padding(horizontal = if (compact) 1.dp else 2.dp) + .semantics { + role = Role.Tab + this.contentDescription = contentDescription + }, + shape = itemShape, + color = containerColor, + contentColor = contentColor, + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = itemPaddingH, vertical = itemPaddingV), + horizontalArrangement = if (selected) Arrangement.Start else Arrangement.Center, + verticalAlignment = Alignment.CenterVertically, + ) { + Icon( + imageVector = icon, + contentDescription = null, + modifier = Modifier.size(iconSize), + ) + if (selected) { + NavigationBarMarqueeText( + text = label, + modifier = Modifier + .weight(1f) + .padding(start = if (compact) 4.dp else 6.dp), + style = labelStyle, + velocity = labelVelocity, + ) + } + } + } +} diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/NavigationBarMarqueeText.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/NavigationBarMarqueeText.kt index 2d8a2ef..1ca1645 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/NavigationBarMarqueeText.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/NavigationBarMarqueeText.kt @@ -7,25 +7,33 @@ import androidx.compose.material3.LocalTextStyle import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.text.TextStyle import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp /** - * Однострочная подпись таба нижней навигации: при нехватке ширины текст - * прокручивается (marquee), без переноса последних букв на вторую строку. + * Однострочная подпись таба: при нехватке ширины текст циклически прокручивается. */ @OptIn(ExperimentalFoundationApi::class) @Composable fun NavigationBarMarqueeText( text: String, modifier: Modifier = Modifier, + style: TextStyle = LocalTextStyle.current, + velocity: Dp = 28.dp, ) { Text( text = text, modifier = modifier .fillMaxWidth() - .basicMarquee(), - style = LocalTextStyle.current, + .basicMarquee( + iterations = Int.MAX_VALUE, + repeatDelayMillis = 1_200, + velocity = velocity, + ), + style = style, maxLines = 1, softWrap = false, overflow = TextOverflow.Clip, diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/WallencScreenScaffold.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/WallencScreenScaffold.kt new file mode 100644 index 0000000..57c47f1 --- /dev/null +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/elements/WallencScreenScaffold.kt @@ -0,0 +1,49 @@ +package com.github.nullptroma.wallenc.ui.elements + +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.padding +import androidx.compose.material3.FabPosition +import androidx.compose.material3.Scaffold +import androidx.compose.material3.SnackbarHost +import androidx.compose.material3.SnackbarHostState +import androidx.compose.runtime.Composable +import androidx.compose.ui.Modifier +import androidx.compose.ui.unit.dp + +@Composable +fun WallencScreenScaffold( + modifier: Modifier = Modifier, + snackbarHostState: SnackbarHostState? = null, + floatingActionButton: @Composable () -> Unit = {}, + content: @Composable (PaddingValues) -> Unit, +) { + Scaffold( + modifier = modifier, + contentWindowInsets = WindowInsets(0.dp), + snackbarHost = { + if (snackbarHostState != null) { + SnackbarHost(snackbarHostState) + } + }, + floatingActionButton = floatingActionButton, + floatingActionButtonPosition = FabPosition.End, + content = content, + ) +} + +@Composable +fun WallencScreenContentPadding( + innerPadding: PaddingValues, + modifier: Modifier = Modifier, + content: @Composable () -> Unit, +) { + Box( + modifier = modifier + .padding(innerPadding) + .padding(horizontal = 16.dp, vertical = 12.dp), + ) { + content() + } +} diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/navigation/MainNavRoutes.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/navigation/MainNavRoutes.kt new file mode 100644 index 0000000..5ff5afb --- /dev/null +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/navigation/MainNavRoutes.kt @@ -0,0 +1,25 @@ +package com.github.nullptroma.wallenc.ui.navigation + +import com.github.nullptroma.wallenc.ui.screens.main.screens.remotes.RemoteVaultsRoute +import com.github.nullptroma.wallenc.ui.screens.main.screens.vault.LocalVaultRoute +import com.github.nullptroma.wallenc.ui.screens.shared.TextEditRoute + +private val mainTopLevelRoutePrefixes: Set = setOf( + LocalVaultRoute::class.qualifiedName!!, + RemoteVaultsRoute::class.qualifiedName!!, +) + +fun isMainTopLevelRoute(route: String?): Boolean { + if (route == null) return true + return mainTopLevelRoutePrefixes.any { route.startsWith(it) } +} + +fun isTextEditDestination(route: String?): Boolean { + val qualified = TextEditRoute::class.qualifiedName ?: return false + return route?.startsWith(qualified) == true +} + +fun shouldShowMainFloatingBack(route: String?): Boolean { + if (route == null) return false + return !isMainTopLevelRoute(route) +} diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/navigation/NavigationState.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/navigation/NavigationState.kt index 73fb084..6e0b114 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/navigation/NavigationState.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/navigation/NavigationState.kt @@ -24,6 +24,10 @@ class NavigationState( restoreState = true } } + + fun pop(): Boolean = navHostController.popBackStack() + + fun canPop(): Boolean = navHostController.previousBackStackEntry != null } @Composable diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/MainScreen.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/MainScreen.kt index f503f0d..0094f9e 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/MainScreen.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/MainScreen.kt @@ -3,34 +3,38 @@ package com.github.nullptroma.wallenc.ui.screens.main import androidx.compose.animation.core.tween import androidx.compose.animation.fadeIn import androidx.compose.animation.fadeOut -import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.foundation.layout.padding import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Cloud import androidx.compose.material.icons.outlined.Folder import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Icon -import androidx.compose.material3.NavigationBar -import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.Scaffold +import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp +import androidx.compose.ui.zIndex import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.toRoute -import com.github.nullptroma.wallenc.ui.elements.NavigationBarMarqueeText import com.github.nullptroma.wallenc.ui.R +import com.github.nullptroma.wallenc.ui.elements.AnimatedFloatingBackButton +import com.github.nullptroma.wallenc.ui.elements.FloatingWallencNavigationBar +import com.github.nullptroma.wallenc.ui.elements.WallencNestedNavBarGap import com.github.nullptroma.wallenc.ui.navigation.NavBarItemData import com.github.nullptroma.wallenc.ui.navigation.NavigationState +import com.github.nullptroma.wallenc.ui.navigation.isMainTopLevelRoute +import com.github.nullptroma.wallenc.ui.navigation.isTextEditDestination +import com.github.nullptroma.wallenc.ui.navigation.shouldShowMainFloatingBack import com.github.nullptroma.wallenc.ui.navigation.rememberNavigationState import com.github.nullptroma.wallenc.ui.screens.main.screens.remotes.RemoteVaultsRoute import com.github.nullptroma.wallenc.ui.screens.main.screens.remotes.RemoteVaultsScreen @@ -54,13 +58,8 @@ import com.github.nullptroma.wallenc.ui.screens.main.screens.vault.VaultBrowserS import com.github.nullptroma.wallenc.ui.screens.shared.TextEditRoute import com.github.nullptroma.wallenc.ui.screens.shared.TextEditScreen -private fun isTextEditDestination(route: String?): Boolean { - val q = TextEditRoute::class.qualifiedName ?: return false - return route?.startsWith(q) == true -} - @OptIn(ExperimentalMaterial3Api::class) -@androidx.compose.runtime.Composable +@Composable fun MainScreen( modifier: Modifier = Modifier, viewModel: MainViewModel = hiltViewModel(), @@ -72,8 +71,12 @@ fun MainScreen( val remoteVaultsViewModel: RemoteVaultsViewModel = hiltViewModel() val childBackStackEntry by navState.navHostController.currentBackStackEntryAsState() - val showWorkStatusBar = !isTextEditDestination(childBackStackEntry?.destination?.route) + val childRoute = childBackStackEntry?.destination?.route + val showWorkStatusBar = !isTextEditDestination(childRoute) + val showMainBottomNav = isMainTopLevelRoute(childRoute) + val showFloatingBack = shouldShowMainFloatingBack(childRoute) && navState.canPop() val workStatus = mainUi.workStatus + val onBack: () -> Unit = { navState.pop() } val topLevelNavBarItems = remember { mapOf( @@ -101,50 +104,37 @@ fun MainScreen( } }, bottomBar = { - Column { - NavigationBar(windowInsets = WindowInsets(0)) { - val navBackStackEntry by navState.navHostController.currentBackStackEntryAsState() - val currentRoute = navBackStackEntry?.destination?.route - topLevelNavBarItems.forEach { - val routeClassName = it.key - val navBarItemData = it.value - val iconVector = navBarItemData.icon - ?: error("Main tab requires icon") - NavigationBarItem( - modifier = Modifier.weight(1f), - icon = { - Icon( - imageVector = iconVector, - contentDescription = stringResource(navBarItemData.iconContentDescriptionResourceId), - ) - }, - label = { - NavigationBarMarqueeText( - text = stringResource(navBarItemData.nameStringResourceId), - ) - }, - selected = currentRoute?.startsWith(routeClassName) == true, - onClick = { - val route = routes[navBarItemData.screenRouteClass] - ?: throw NullPointerException("Route ${navBarItemData.screenRouteClass} not found") - if (currentRoute?.startsWith(routeClassName) != true) { - navState.changeTop(route) - } - }, - ) - } + if (showMainBottomNav) { + Box( + modifier = Modifier + .padding(horizontal = 16.dp) + .padding(top = WallencNestedNavBarGap, bottom = WallencNestedNavBarGap), + ) { + FloatingWallencNavigationBar( + compact = true, + items = topLevelNavBarItems, + routes = routes, + currentRoute = childRoute, + onNavigate = { item -> + val route = routes[item.screenRouteClass] + ?: error("Route ${item.screenRouteClass} not found") + navState.changeTop(route) + }, + ) } - HorizontalDivider() } }, ) { innerPaddings -> - NavHost( - navController = navState.navHostController, - startDestination = routes[LocalVaultRoute::class.qualifiedName]!!, + Box( modifier = Modifier .fillMaxSize() .padding(innerPaddings), ) { + NavHost( + navController = navState.navHostController, + startDestination = routes[LocalVaultRoute::class.qualifiedName]!!, + modifier = Modifier.fillMaxSize(), + ) { composable( enterTransition = { fadeIn(tween(200)) }, exitTransition = { fadeOut(tween(200)) }, @@ -242,9 +232,7 @@ fun MainScreen( ), ) }, - onDeleted = { - navState.navHostController.popBackStack() - }, + onDeleted = { navState.pop() }, ) } composable( @@ -255,7 +243,7 @@ fun MainScreen( TextSecretEditScreen( onSaved = { savedSecretId -> val editingExisting = route.secretId != null - navState.navHostController.popBackStack() + navState.pop() if (!editingExisting) { navState.push( TextSecretDetailsRoute( @@ -269,8 +257,18 @@ fun MainScreen( } composable { val route: TextEditRoute = it.toRoute() - TextEditScreen(route.text) + TextEditScreen(text = route.text) } + } + AnimatedFloatingBackButton( + visible = showFloatingBack, + onClick = onBack, + modifier = Modifier + .zIndex(1f) + .align(Alignment.BottomStart) + .navigationBarsPadding() + .padding(start = 12.dp, bottom = 12.dp), + ) } } } diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/StorageHomeScreen.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/StorageHomeScreen.kt index 4ea1f39..e119433 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/StorageHomeScreen.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/StorageHomeScreen.kt @@ -4,7 +4,6 @@ import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -16,7 +15,6 @@ import androidx.compose.material3.CardDefaults import androidx.compose.material3.CircularProgressIndicator import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue @@ -27,6 +25,8 @@ import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.github.nullptroma.wallenc.ui.R +import com.github.nullptroma.wallenc.ui.elements.WallencScreenContentPadding +import com.github.nullptroma.wallenc.ui.elements.WallencScreenScaffold import com.github.nullptroma.wallenc.ui.resources.resolveText @Composable @@ -38,15 +38,10 @@ fun StorageHomeScreen( ) { val uiState by viewModel.state.collectAsStateWithLifecycle() - Scaffold( - modifier = modifier, - contentWindowInsets = WindowInsets(0.dp), - ) { innerPadding -> + WallencScreenScaffold(modifier = modifier) { innerPadding -> + WallencScreenContentPadding(innerPadding) { Column( - modifier = Modifier - .fillMaxSize() - .padding(innerPadding) - .padding(16.dp), + modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy(12.dp), ) { if (uiState.isLoading) { @@ -110,6 +105,7 @@ fun StorageHomeScreen( color = MaterialTheme.colorScheme.onSurfaceVariant, ) } + } } } diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretDetailsScreen.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretDetailsScreen.kt index 8aa4126..0c65edc 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretDetailsScreen.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretDetailsScreen.kt @@ -4,7 +4,6 @@ import android.content.ClipData import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -19,7 +18,6 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -33,6 +31,8 @@ import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.github.nullptroma.wallenc.ui.R +import com.github.nullptroma.wallenc.ui.elements.WallencScreenContentPadding +import com.github.nullptroma.wallenc.ui.elements.WallencScreenScaffold import com.github.nullptroma.wallenc.ui.resources.resolveText import kotlinx.coroutines.launch @@ -48,15 +48,10 @@ fun TextSecretDetailsScreen( val clipboard = LocalClipboard.current val scope = rememberCoroutineScope() - Scaffold( - modifier = modifier, - contentWindowInsets = WindowInsets(0.dp), - ) { innerPadding -> + WallencScreenScaffold(modifier = modifier) { innerPadding -> + WallencScreenContentPadding(innerPadding) { Column( - modifier = Modifier - .fillMaxSize() - .padding(innerPadding) - .padding(16.dp), + modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy(10.dp), ) { uiState.errorNotification?.let { notification -> @@ -144,5 +139,6 @@ fun TextSecretDetailsScreen( Text(stringResource(R.string.remove)) } } + } } } diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretEditScreen.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretEditScreen.kt index 98e5b9a..b43056b 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretEditScreen.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretEditScreen.kt @@ -3,7 +3,6 @@ package com.github.nullptroma.wallenc.ui.screens.main.screens.storage.secrets import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding @@ -16,7 +15,6 @@ import androidx.compose.material3.Icon import androidx.compose.material3.IconButton import androidx.compose.material3.LinearProgressIndicator import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.material3.TextButton import androidx.compose.runtime.Composable @@ -33,6 +31,8 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.github.nullptroma.wallenc.domain.datatypes.TextSecretEntryRecord import com.github.nullptroma.wallenc.ui.R +import com.github.nullptroma.wallenc.ui.elements.WallencScreenContentPadding +import com.github.nullptroma.wallenc.ui.elements.WallencScreenScaffold import com.github.nullptroma.wallenc.ui.resources.resolveText @Composable @@ -58,15 +58,10 @@ fun TextSecretEditScreen( } } - Scaffold( - modifier = modifier, - contentWindowInsets = WindowInsets(0.dp), - ) { innerPadding -> + WallencScreenScaffold(modifier = modifier) { innerPadding -> + WallencScreenContentPadding(innerPadding) { Column( - modifier = Modifier - .fillMaxSize() - .padding(innerPadding) - .padding(16.dp), + modifier = Modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy(10.dp), ) { Text( @@ -155,5 +150,6 @@ fun TextSecretEditScreen( } } } + } } } diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretsScreen.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretsScreen.kt index 1953575..6f5bcab 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretsScreen.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/secrets/TextSecretsScreen.kt @@ -1,7 +1,6 @@ package com.github.nullptroma.wallenc.ui.screens.main.screens.storage.secrets import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn @@ -10,7 +9,6 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Add import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.Icon -import androidx.compose.material3.Scaffold import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.ui.Modifier @@ -21,6 +19,8 @@ import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.github.nullptroma.wallenc.domain.datatypes.TextSecretRecord import com.github.nullptroma.wallenc.ui.R +import com.github.nullptroma.wallenc.ui.elements.WallencScreenContentPadding +import com.github.nullptroma.wallenc.ui.elements.WallencScreenScaffold @Composable fun TextSecretsScreen( @@ -31,9 +31,8 @@ fun TextSecretsScreen( ) { val uiState by viewModel.state.collectAsStateWithLifecycle() - Scaffold( + WallencScreenScaffold( modifier = modifier, - contentWindowInsets = WindowInsets(0.dp), floatingActionButton = { FloatingActionButton( onClick = { @@ -47,11 +46,10 @@ fun TextSecretsScreen( } }, ) { innerPadding -> + WallencScreenContentPadding(innerPadding) { TextSecretsScreenContent( uiState = uiState, - modifier = Modifier - .fillMaxSize() - .padding(innerPadding), + modifier = Modifier.fillMaxSize(), ) { LazyColumn(verticalArrangement = Arrangement.spacedBy(10.dp)) { items(uiState.items) { secret -> @@ -63,5 +61,6 @@ fun TextSecretsScreen( } } } + } } } diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/twofa/TwoFaTokensScreen.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/twofa/TwoFaTokensScreen.kt index 4b5e195..0e2fc87 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/twofa/TwoFaTokensScreen.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/twofa/TwoFaTokensScreen.kt @@ -79,6 +79,8 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.github.nullptroma.wallenc.domain.datatypes.TwoFaTokenRecord import com.github.nullptroma.wallenc.ui.R import com.github.nullptroma.wallenc.ui.elements.QrScannerDialog +import com.github.nullptroma.wallenc.ui.elements.WallencScreenContentPadding +import com.github.nullptroma.wallenc.ui.elements.WallencScreenScaffold import com.github.nullptroma.wallenc.usecases.TwoFaCodeState import com.github.nullptroma.wallenc.usecases.buildTwoFaCodeState import kotlinx.coroutines.delay @@ -103,9 +105,8 @@ fun TwoFaTokensScreen( var editingToken by remember { mutableStateOf(null) } var creating by remember { mutableStateOf(false) } - Scaffold( + WallencScreenScaffold( modifier = modifier, - contentWindowInsets = WindowInsets(0.dp), floatingActionButton = { FloatingActionButton( onClick = { @@ -119,11 +120,10 @@ fun TwoFaTokensScreen( } }, ) { innerPadding -> + WallencScreenContentPadding(innerPadding) { TwoFaTokensScreenContent( uiState = uiState, - modifier = Modifier - .fillMaxSize() - .padding(innerPadding), + modifier = Modifier.fillMaxSize(), ) { LazyColumn(verticalArrangement = Arrangement.spacedBy(10.dp)) { items(uiState.items) { item -> @@ -244,6 +244,7 @@ fun TwoFaTokensScreen( } } } + } } } diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/twofa/TwoFaTokensScreenContent.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/twofa/TwoFaTokensScreenContent.kt index 0028768..d63729c 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/twofa/TwoFaTokensScreenContent.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/storage/twofa/TwoFaTokensScreenContent.kt @@ -27,9 +27,7 @@ fun TwoFaTokensScreenContent( tokenList: @Composable () -> Unit = {}, ) { Column( - modifier = modifier - .fillMaxSize() - .padding(16.dp), + modifier = modifier.fillMaxSize(), verticalArrangement = Arrangement.spacedBy(12.dp), ) { if (uiState.isLoading) { diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/vault/VaultBrowserScreen.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/vault/VaultBrowserScreen.kt index 06ab4c5..61c1ad5 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/vault/VaultBrowserScreen.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/main/screens/vault/VaultBrowserScreen.kt @@ -80,32 +80,29 @@ fun VaultBrowserScreen( val showEmptyState = uiState.storagesList.isEmpty() && !uiState.storagesRefreshing val isUuidBusy: (UUID) -> Boolean = { uuid -> uuid in uiState.busyStorageUuids } - Box { - Scaffold( - modifier = modifier, - contentWindowInsets = WindowInsets(0.dp), - floatingActionButton = { - FloatingActionButton( - onClick = { - if (fabEnabled && !fabBusy) { - viewModel.createStorage() - } - }, - modifier = Modifier.alpha(if (fabEnabled && !fabBusy) 1f else 0.38f), - ) { - Icon( - Icons.Filled.Add, - contentDescription = stringResource( - when { - !fabEnabled -> R.string.vault_fab_add_storage_disabled_cd - fabBusy -> R.string.vault_fab_add_storage_busy_cd - else -> R.string.vault_fab_add_storage_cd - }, - ), - ) + val addFab: @Composable () -> Unit = { + FloatingActionButton( + onClick = { + if (fabEnabled && !fabBusy) { + viewModel.createStorage() } }, - ) { innerPadding -> + modifier = Modifier.alpha(if (fabEnabled && !fabBusy) 1f else 0.38f), + ) { + Icon( + Icons.Filled.Add, + contentDescription = stringResource( + when { + !fabEnabled -> R.string.vault_fab_add_storage_disabled_cd + fabBusy -> R.string.vault_fab_add_storage_busy_cd + else -> R.string.vault_fab_add_storage_cd + }, + ), + ) + } + } + + val vaultContent: @Composable (androidx.compose.foundation.layout.PaddingValues) -> Unit = { innerPadding -> Column( modifier = Modifier .padding(innerPadding) @@ -178,7 +175,14 @@ fun VaultBrowserScreen( } } } - } + } + + Box(modifier = modifier) { + Scaffold( + contentWindowInsets = WindowInsets(0.dp), + floatingActionButton = addFab, + content = vaultContent, + ) if (showFullscreenLoader) { Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/shared/TextEditScreen.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/shared/TextEditScreen.kt index fd151ff..f60345a 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/shared/TextEditScreen.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/shared/TextEditScreen.kt @@ -1,19 +1,25 @@ package com.github.nullptroma.wallenc.ui.screens.shared -import androidx.compose.foundation.layout.padding import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource -import androidx.compose.ui.unit.dp import com.github.nullptroma.wallenc.ui.R +import com.github.nullptroma.wallenc.ui.elements.WallencScreenContentPadding +import com.github.nullptroma.wallenc.ui.elements.WallencScreenScaffold @Composable -fun TextEditScreen(text: String) { - Text( - text = stringResource(R.string.text_edit_screen_placeholder, text), - style = MaterialTheme.typography.bodyLarge, - modifier = Modifier.padding(16.dp), - ) +fun TextEditScreen( + text: String, + modifier: Modifier = Modifier, +) { + WallencScreenScaffold(modifier = modifier) { innerPadding -> + WallencScreenContentPadding(innerPadding) { + Text( + text = stringResource(R.string.text_edit_screen_placeholder, text), + style = MaterialTheme.typography.bodyLarge, + ) + } + } } diff --git a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/sync/StorageSyncScreen.kt b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/sync/StorageSyncScreen.kt index 18b8932..0f9e81b 100644 --- a/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/sync/StorageSyncScreen.kt +++ b/ui/src/main/java/com/github/nullptroma/wallenc/ui/screens/sync/StorageSyncScreen.kt @@ -1,6 +1,7 @@ package com.github.nullptroma.wallenc.ui.screens.sync import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.WindowInsets @@ -39,6 +40,7 @@ import androidx.compose.runtime.remember import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.foundation.layout.navigationBarsPadding import androidx.compose.ui.draw.alpha import androidx.compose.ui.res.stringResource import androidx.compose.ui.text.font.FontFamily @@ -46,6 +48,7 @@ import androidx.compose.ui.unit.dp import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.github.nullptroma.wallenc.ui.R +import com.github.nullptroma.wallenc.ui.elements.AnimatedFloatingBackButton import com.github.nullptroma.wallenc.ui.resources.UserNotification import com.github.nullptroma.wallenc.domain.interfaces.StorageSyncGroupEncryptionKind import java.util.UUID @@ -441,28 +444,21 @@ private fun StoragePickerScreen( contentWindowInsets = WindowInsets(0.dp), snackbarHost = { SnackbarHost(snackbarHostState) }, ) { inner -> - Column( + Box( modifier = Modifier .padding(inner) - .fillMaxSize() - .padding(16.dp), - verticalArrangement = Arrangement.spacedBy(12.dp), + .fillMaxSize(), ) { - Row( - horizontalArrangement = Arrangement.spacedBy(8.dp), - verticalAlignment = Alignment.CenterVertically, + Column( + modifier = Modifier + .fillMaxSize() + .padding(horizontal = 16.dp, vertical = 12.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), ) { - IconButton(onClick = onBack) { - Icon( - Icons.AutoMirrored.Rounded.ArrowBack, - contentDescription = stringResource(R.string.sync_cd_picker_back), - ) - } Text( text = stringResource(id = R.string.sync_picker_title, groupId), - style = MaterialTheme.typography.titleMedium, + style = MaterialTheme.typography.titleLarge, ) - } LazyColumn( modifier = Modifier.fillMaxSize(), @@ -539,6 +535,15 @@ private fun StoragePickerScreen( } } } + } + AnimatedFloatingBackButton( + visible = true, + onClick = onBack, + modifier = Modifier + .align(Alignment.BottomStart) + .navigationBarsPadding() + .padding(start = 12.dp, bottom = 12.dp), + ) } } } diff --git a/ui/src/main/res/values-ru/strings.xml b/ui/src/main/res/values-ru/strings.xml index 3756e67..03a7c00 100644 --- a/ui/src/main/res/values-ru/strings.xml +++ b/ui/src/main/res/values-ru/strings.xml @@ -7,6 +7,7 @@ Главная Синхронизация Настройки + Назад Статус: Выполняется задач: %1$d Сканирование vault: загрузка списка хранилищ… diff --git a/ui/src/main/res/values/strings.xml b/ui/src/main/res/values/strings.xml index 0e68d23..5a5e01b 100644 --- a/ui/src/main/res/values/strings.xml +++ b/ui/src/main/res/values/strings.xml @@ -7,6 +7,12 @@ Home Sync Settings + Go back + Remote vault + Storage + 2FA tokens + Text secrets + Text Status: Running tasks: %1$d Scanning vault: loading storage list…