mirror of
https://github.com/fromchat-messenger/app.git
synced 2026-09-22 19:15:05 +03:00
Improve register and fix about app
Signed-off-by: denis0001-dev <denis0001.dev@ya.ru>
This commit is contained in:
@@ -358,7 +358,6 @@ fun App(
|
||||
val widthSizeClass = currentWindowAdaptiveInfo().widthSizeClass
|
||||
val isDesktopListDetail = widthSizeClass != WindowWidthSizeClass.COMPACT
|
||||
var pendingMainTab by remember { mutableIntStateOf(MAIN_PAGE_CHATS) }
|
||||
var pendingPreAuthSettingsDetailRoute by remember { mutableStateOf<String?>(null) }
|
||||
|
||||
// Handle startup/deep-link navigation targets (profile links)
|
||||
LaunchedEffect(
|
||||
@@ -504,21 +503,19 @@ fun App(
|
||||
LaunchedEffect(Unit) {
|
||||
DesktopMenuCommands.commands.collect { command ->
|
||||
if (command != DesktopMenuCommand.OpenAbout) return@collect
|
||||
pendingMainTab = MAIN_PAGE_SETTINGS
|
||||
if (isDesktopListDetail) {
|
||||
// Logged-out: same root About as Welcome/auth overflow — never mount
|
||||
// the empty chat list–detail shell.
|
||||
if (ApiClient.token.isNullOrBlank()) {
|
||||
// Pre-auth: the settings detail NavHost isn't mounted until the root
|
||||
// shell is visible, so we defer settings navigation until `currentRoute`
|
||||
// becomes `chat`.
|
||||
pendingPreAuthSettingsDetailRoute = SettingsRoutes.About
|
||||
navController.navigate("chat") {
|
||||
navController.navigate(SettingsRoutes.About) {
|
||||
launchSingleTop = true
|
||||
}
|
||||
} else {
|
||||
return@collect
|
||||
}
|
||||
pendingMainTab = MAIN_PAGE_SETTINGS
|
||||
if (isDesktopListDetail) {
|
||||
settingsDetailNavController.navigateReplacingMainDetail(
|
||||
route = SettingsRoutes.About,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
navController.navigateReplacingMainDetail(
|
||||
route = SettingsRoutes.About,
|
||||
@@ -527,14 +524,6 @@ fun App(
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(currentRoute, pendingPreAuthSettingsDetailRoute) {
|
||||
val route = pendingPreAuthSettingsDetailRoute ?: return@LaunchedEffect
|
||||
if (!isDesktopListDetail) return@LaunchedEffect
|
||||
if (currentRoute != "chat") return@LaunchedEffect
|
||||
settingsDetailNavController.navigateReplacingMainDetail(route = route)
|
||||
pendingPreAuthSettingsDetailRoute = null
|
||||
}
|
||||
|
||||
ScreenSurface {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
@Composable
|
||||
|
||||
@@ -4,36 +4,25 @@ import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.union
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Info
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.outlined.BugReport
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -41,20 +30,14 @@ import coil3.compose.AsyncImage
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import ru.fromchat.Res
|
||||
import ru.fromchat.api.ApiClient
|
||||
import ru.fromchat.about
|
||||
import ru.fromchat.auth_get_started
|
||||
import ru.fromchat.auth_welcome_tagline
|
||||
import ru.fromchat.auth_welcome_title
|
||||
import ru.fromchat.logs_title
|
||||
import ru.fromchat.more
|
||||
import ru.fromchat.ui.auth.PreAuthOverflowMenu
|
||||
import ru.fromchat.ui.components.ActionButton
|
||||
import ru.fromchat.ui.components.Text
|
||||
import ru.fromchat.ui.main.settings.SettingsRoutes
|
||||
import ru.fromchat.ui.main.settings.SettingsStepHorizontalPadding
|
||||
|
||||
private val AUTH_CONTENT_MAX_WIDTH = 600.dp
|
||||
private val AUTH_CONTENT_MAX_HEIGHT = 800.dp
|
||||
|
||||
@Composable
|
||||
fun WelcomeScreen(
|
||||
onGetStarted: () -> Unit,
|
||||
@@ -67,70 +50,36 @@ fun WelcomeScreen(
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
containerColor = Color.Transparent,
|
||||
contentWindowInsets = WindowInsets.safeDrawing.union(WindowInsets.extraStatusBars),
|
||||
) { innerPadding ->
|
||||
val navController = LocalNavController.current
|
||||
var menuExpanded by remember { mutableStateOf(false) }
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(innerPadding),
|
||||
) {
|
||||
Box(modifier = Modifier.align(Alignment.TopEnd)) {
|
||||
IconButton(onClick = { menuExpanded = true }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
contentDescription = stringResource(Res.string.more),
|
||||
)
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = menuExpanded,
|
||||
onDismissRequest = { menuExpanded = false },
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(Res.string.about)) },
|
||||
leadingIcon = {
|
||||
Icon(Icons.Default.Info, contentDescription = null)
|
||||
},
|
||||
onClick = {
|
||||
menuExpanded = false
|
||||
navController.navigate(SettingsRoutes.About)
|
||||
},
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(Res.string.logs_title)) },
|
||||
leadingIcon = {
|
||||
Icon(Icons.Outlined.BugReport, contentDescription = null)
|
||||
},
|
||||
onClick = {
|
||||
menuExpanded = false
|
||||
navController.navigate(SettingsRoutes.Logs)
|
||||
},
|
||||
)
|
||||
}
|
||||
PreAuthOverflowMenu()
|
||||
}
|
||||
|
||||
BoxWithConstraints(modifier = Modifier.fillMaxSize()) {
|
||||
val constrainWide = maxWidth > AUTH_CONTENT_MAX_WIDTH
|
||||
Box(
|
||||
modifier =
|
||||
if (constrainWide) {
|
||||
BoxWithConstraints(Modifier.fillMaxSize()) {
|
||||
val wide = maxWidth > 600.dp
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.align(if (wide) Alignment.Center else Alignment.TopCenter)
|
||||
.then(
|
||||
if (wide) {
|
||||
Modifier
|
||||
.align(Alignment.Center)
|
||||
.widthIn(max = AUTH_CONTENT_MAX_WIDTH)
|
||||
.heightIn(max = AUTH_CONTENT_MAX_HEIGHT)
|
||||
.widthIn(max = 600.dp)
|
||||
.heightIn(max = 800.dp)
|
||||
} else {
|
||||
Modifier.fillMaxSize()
|
||||
},
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
)
|
||||
.navigationBarsPadding()
|
||||
.padding(horizontal = SettingsStepHorizontalPadding)
|
||||
.padding(bottom = 16.dp),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
verticalArrangement = if (wide) Arrangement.Center else Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
) {
|
||||
AsyncImage(
|
||||
@@ -147,6 +96,7 @@ fun WelcomeScreen(
|
||||
Text(
|
||||
text = stringResource(Res.string.auth_welcome_title),
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
|
||||
@@ -169,4 +119,3 @@ fun WelcomeScreen(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,106 @@
|
||||
package ru.fromchat.ui.auth
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxScope
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Info
|
||||
import androidx.compose.material.icons.filled.MoreVert
|
||||
import androidx.compose.material.icons.outlined.BugReport
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import ru.fromchat.Res
|
||||
import ru.fromchat.about
|
||||
import ru.fromchat.logs_title
|
||||
import ru.fromchat.more
|
||||
import ru.fromchat.ui.LocalNavController
|
||||
import ru.fromchat.ui.components.Text
|
||||
import ru.fromchat.ui.main.settings.SettingsRoutes
|
||||
|
||||
/** Margin between the window edge and the wide [AuthContentFrame] panel. */
|
||||
val AuthWidePanelOuterPadding = 32.dp
|
||||
|
||||
/**
|
||||
* Centers auth content on wide layouts (max 600×800.dp, [AuthWidePanelOuterPadding] margin).
|
||||
* Narrow layouts fill the window ([widePanel] = false).
|
||||
*/
|
||||
@Composable
|
||||
fun AuthContentFrame(
|
||||
modifier: Modifier = Modifier,
|
||||
content: @Composable BoxScope.(widePanel: Boolean) -> Unit,
|
||||
) {
|
||||
BoxWithConstraints(modifier.fillMaxSize()) {
|
||||
val widePanel = maxWidth > 600.dp
|
||||
if (widePanel) {
|
||||
Box(
|
||||
Modifier
|
||||
.align(Alignment.Center)
|
||||
.padding(AuthWidePanelOuterPadding)
|
||||
.widthIn(max = 600.dp)
|
||||
.heightIn(max = 800.dp),
|
||||
) {
|
||||
content(true)
|
||||
}
|
||||
} else {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
content(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Overflow menu shared by welcome and auth steps (About, Logs). */
|
||||
@Composable
|
||||
fun PreAuthOverflowMenu() {
|
||||
val navController = LocalNavController.current
|
||||
var menuExpanded by remember { mutableStateOf(false) }
|
||||
|
||||
Box {
|
||||
IconButton(onClick = { menuExpanded = true }) {
|
||||
Icon(
|
||||
imageVector = Icons.Default.MoreVert,
|
||||
contentDescription = stringResource(Res.string.more),
|
||||
)
|
||||
}
|
||||
DropdownMenu(
|
||||
expanded = menuExpanded,
|
||||
onDismissRequest = { menuExpanded = false },
|
||||
) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(Res.string.about)) },
|
||||
leadingIcon = {
|
||||
Icon(Icons.Default.Info, contentDescription = null)
|
||||
},
|
||||
onClick = {
|
||||
menuExpanded = false
|
||||
navController.navigate(SettingsRoutes.About)
|
||||
},
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(stringResource(Res.string.logs_title)) },
|
||||
leadingIcon = {
|
||||
Icon(Icons.Outlined.BugReport, contentDescription = null)
|
||||
},
|
||||
onClick = {
|
||||
menuExpanded = false
|
||||
navController.navigate(SettingsRoutes.Logs)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
package ru.fromchat.ui.auth
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.layout.union
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Storage
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
@@ -18,7 +20,6 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.runtime.snapshotFlow
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import com.pr0gramm3r101.utils.crypto.deriveAuthSecret
|
||||
@@ -53,6 +54,7 @@ import ru.fromchat.ui.components.Text
|
||||
import ru.fromchat.ui.components.TextCta
|
||||
import ru.fromchat.ui.components.rememberExpressiveStepFlow
|
||||
import ru.fromchat.ui.components.showLoggedSnackbar
|
||||
import ru.fromchat.ui.extraStatusBars
|
||||
import ru.fromchat.ui.main.settings.SettingsStepHorizontalPadding
|
||||
import kotlin.time.Duration.Companion.milliseconds
|
||||
|
||||
@@ -85,9 +87,6 @@ internal sealed interface RegisterResult {
|
||||
|
||||
private const val AUTH_SERVER_PROBE_TIMEOUT_MS = 5_000L
|
||||
|
||||
private val AUTH_CONTENT_MAX_WIDTH = 600.dp
|
||||
private val AUTH_CONTENT_MAX_HEIGHT = 800.dp
|
||||
|
||||
internal suspend fun probeCurrentServer() = runCatching {
|
||||
withTimeout(AUTH_SERVER_PROBE_TIMEOUT_MS.milliseconds) {
|
||||
probeServer(Settings.readServerConfig()) is ServerProbeResult.Supported
|
||||
@@ -443,19 +442,13 @@ fun AuthScreen(
|
||||
}
|
||||
|
||||
val yandexStep = yandexParams
|
||||
BoxWithConstraints(modifier = Modifier.fillMaxSize()) {
|
||||
val constrainWide = maxWidth > AUTH_CONTENT_MAX_WIDTH
|
||||
// Clear title bar / system bars outside the rounded panel (same as WelcomeScreen).
|
||||
Box(
|
||||
modifier =
|
||||
if (constrainWide) {
|
||||
Modifier
|
||||
.align(Alignment.Center)
|
||||
.widthIn(max = AUTH_CONTENT_MAX_WIDTH)
|
||||
.heightIn(max = AUTH_CONTENT_MAX_HEIGHT)
|
||||
} else {
|
||||
Modifier.fillMaxSize()
|
||||
},
|
||||
.fillMaxSize()
|
||||
.windowInsetsPadding(WindowInsets.safeDrawing.union(WindowInsets.extraStatusBars)),
|
||||
) {
|
||||
AuthContentFrame { _ ->
|
||||
ExpressiveStepFlowScaffold(
|
||||
flowState = flowState,
|
||||
pages = listOf(
|
||||
@@ -566,6 +559,9 @@ fun AuthScreen(
|
||||
),
|
||||
snackbarHostState = snackbarHostState,
|
||||
onBackAtFirstPage = wrappedBackToWelcome,
|
||||
trailingTopBarActions = { PreAuthOverflowMenu() },
|
||||
contentBackground = MaterialTheme.colorScheme.background,
|
||||
topBarWindowInsets = WindowInsets(0, 0, 0, 0),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -343,6 +343,8 @@ fun ExpressiveStepFlowScaffold(
|
||||
onBackAtFirstPage: () -> Unit,
|
||||
trailingTopBarActions: @Composable (() -> Unit)? = null,
|
||||
hazeScaffold: Boolean = true,
|
||||
contentBackground: Color = MaterialTheme.colorScheme.background,
|
||||
topBarWindowInsets: WindowInsets = WindowInsets.extraStatusBars,
|
||||
) {
|
||||
val pagerState = flowState.pagerState
|
||||
val scope = flowState.scope
|
||||
@@ -551,7 +553,7 @@ fun ExpressiveStepFlowScaffold(
|
||||
fun HazeTopBar(hazeState: HazeState) {
|
||||
val topBarHazeStyle = HazeMaterials.thin()
|
||||
TopAppBar(
|
||||
windowInsets = WindowInsets.extraStatusBars,
|
||||
windowInsets = topBarWindowInsets,
|
||||
title = {},
|
||||
navigationIcon = {
|
||||
IconButton(onClick = navigateBack) {
|
||||
@@ -585,7 +587,7 @@ fun ExpressiveStepFlowScaffold(
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.windowInsetsPadding(WindowInsets.extraStatusBars)
|
||||
.windowInsetsPadding(topBarWindowInsets)
|
||||
.padding(horizontal = 4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
@@ -648,7 +650,7 @@ fun ExpressiveStepFlowScaffold(
|
||||
state = listState,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(scheme.background)
|
||||
.background(contentBackground)
|
||||
.hazeSource(hazeState)
|
||||
.onGloballyPositioned {
|
||||
listViewportBounds = it.boundsInWindow()
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package ru.fromchat.ui.components
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
@@ -11,6 +13,7 @@ import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.chrisbanes.haze.HazeState
|
||||
import dev.chrisbanes.haze.blur.HazeBlurStyle
|
||||
@@ -25,6 +28,7 @@ fun HazeBottomBar(
|
||||
hazeState: HazeState,
|
||||
modifier: Modifier = Modifier,
|
||||
hazeStyle: HazeBlurStyle? = null,
|
||||
baseColor: Color? = null,
|
||||
content: @Composable () -> Unit,
|
||||
) {
|
||||
val resolvedStyle = hazeStyle ?: HazeMaterials.thin()
|
||||
@@ -32,6 +36,10 @@ fun HazeBottomBar(
|
||||
Modifier.hazeEffect(state = hazeState) {
|
||||
blurEffect {
|
||||
style = resolvedStyle
|
||||
progressive = HazeProgressive.verticalGradient(
|
||||
startIntensity = 0f,
|
||||
endIntensity = 1f,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -49,9 +57,21 @@ fun HazeBottomBar(
|
||||
modifier = Modifier
|
||||
.windowInsetsPadding(WindowInsets.ime)
|
||||
.fillMaxWidth()
|
||||
.then(effectModifier)
|
||||
.then(modifier),
|
||||
) {
|
||||
Box(Modifier.fillMaxWidth()) {
|
||||
if (baseColor != null) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
.background(baseColor),
|
||||
)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.matchParentSize()
|
||||
.then(effectModifier),
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
@@ -63,6 +83,7 @@ fun HazeBottomBar(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun HazeActionButton(
|
||||
@@ -71,12 +92,18 @@ fun HazeActionButton(
|
||||
modifier: Modifier = Modifier,
|
||||
innerModifier: Modifier = Modifier,
|
||||
hazeStyle: HazeBlurStyle? = null,
|
||||
baseColor: Color? = null,
|
||||
enabled: Boolean = true,
|
||||
loading: Boolean = false,
|
||||
interactionSource: MutableInteractionSource? = null,
|
||||
content: @Composable (RowScope.() -> Unit)
|
||||
) {
|
||||
HazeBottomBar(hazeState = hazeState, modifier = modifier, hazeStyle = hazeStyle) {
|
||||
HazeBottomBar(
|
||||
hazeState = hazeState,
|
||||
modifier = modifier,
|
||||
hazeStyle = hazeStyle,
|
||||
baseColor = baseColor,
|
||||
) {
|
||||
ActionButton(
|
||||
onClick = onClick,
|
||||
modifier = innerModifier,
|
||||
|
||||
@@ -2,6 +2,7 @@ package ru.fromchat.ui.main.settings
|
||||
|
||||
import androidx.compose.animation.core.AnimationSpec
|
||||
import androidx.compose.animation.core.animate
|
||||
import androidx.compose.foundation.layout.WindowInsets
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
@@ -32,6 +33,8 @@ import kotlinx.coroutines.launch
|
||||
import org.jetbrains.compose.resources.stringResource
|
||||
import ru.fromchat.Res
|
||||
import ru.fromchat.back
|
||||
import ru.fromchat.ui.extraStatusBars
|
||||
import ru.fromchat.ui.main.LocalConversationListDetailActive
|
||||
import ru.fromchat.ui.main.detailPaneShowBackButton
|
||||
|
||||
/** Idle gap after mouse-wheel / trackpad deltas before snapping the collapsing bar. */
|
||||
@@ -85,11 +88,19 @@ fun SettingsDetailTopBar(
|
||||
// `surfaceContainerLowest` — that token reads as a separate pane fill in
|
||||
// two-pane against the list–detail shell.
|
||||
val paneColor = MaterialTheme.colorScheme.background
|
||||
// List–detail shell already pads/consumes extraStatusBars; full-screen settings
|
||||
// (e.g. logged-out About) need the desktop title-bar inset explicitly.
|
||||
val topBarWindowInsets = if (LocalConversationListDetailActive.current) {
|
||||
WindowInsets(0, 0, 0, 0)
|
||||
} else {
|
||||
WindowInsets.extraStatusBars
|
||||
}
|
||||
if (settingsDetailUseCollapsingTopBar()) {
|
||||
MediumTopAppBar(
|
||||
title = title,
|
||||
navigationIcon = navigationIcon,
|
||||
scrollBehavior = scrollBehavior,
|
||||
windowInsets = topBarWindowInsets,
|
||||
// Unscrolled: pane. Collapsed: default scrolledContainerColor (elevation).
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = paneColor,
|
||||
@@ -100,6 +111,7 @@ fun SettingsDetailTopBar(
|
||||
TopAppBar(
|
||||
title = title,
|
||||
navigationIcon = navigationIcon,
|
||||
windowInsets = topBarWindowInsets,
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = paneColor,
|
||||
scrolledContainerColor = paneColor,
|
||||
|
||||
+29
-4
@@ -19,7 +19,10 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.navigationBars
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.safeDrawing
|
||||
import androidx.compose.foundation.layout.statusBars
|
||||
import androidx.compose.foundation.layout.union
|
||||
import androidx.compose.foundation.layout.windowInsetsPadding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.text.KeyboardOptions
|
||||
@@ -108,6 +111,8 @@ import ru.fromchat.server_config_unsupported_no_instance_id
|
||||
import ru.fromchat.server_ip_hint
|
||||
import ru.fromchat.server_ip_label
|
||||
import ru.fromchat.ui.LocalNavController
|
||||
import ru.fromchat.ui.auth.AuthContentFrame
|
||||
import ru.fromchat.ui.extraStatusBars
|
||||
import ru.fromchat.ui.components.CtaShape
|
||||
import ru.fromchat.ui.components.DisabledBringIntoViewSpec
|
||||
import ru.fromchat.ui.components.ExpressiveIconFrame
|
||||
@@ -231,7 +236,12 @@ fun ServerConfigScreen() {
|
||||
disabledContainerColor = Color.Transparent,
|
||||
)
|
||||
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
val preAuth = ApiClient.token.isNullOrBlank()
|
||||
|
||||
@Composable
|
||||
fun ServerConfigContent() {
|
||||
val listBackground = scheme.background
|
||||
|
||||
Scaffold(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentWindowInsets = WindowInsets.navigationBars,
|
||||
@@ -346,7 +356,7 @@ fun ServerConfigScreen() {
|
||||
},
|
||||
colors = TopAppBarDefaults.topAppBarColors(
|
||||
containerColor = Color.Transparent,
|
||||
scrolledContainerColor = Color.Transparent
|
||||
scrolledContainerColor = Color.Transparent,
|
||||
),
|
||||
modifier = Modifier.hazeEffect(state = actionHazeState) {
|
||||
blurEffect {
|
||||
@@ -356,7 +366,7 @@ fun ServerConfigScreen() {
|
||||
endIntensity = 0f,
|
||||
)
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
) { innerPadding ->
|
||||
@@ -379,7 +389,7 @@ fun ServerConfigScreen() {
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.consumeWindowInsets(innerPadding)
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.background(listBackground)
|
||||
.hazeSource(actionHazeState)
|
||||
.onGloballyPositioned { listViewportBounds = it.boundsInWindow() },
|
||||
contentPadding = PaddingValues(bottom = bottomInsetPadding),
|
||||
@@ -422,6 +432,7 @@ fun ServerConfigScreen() {
|
||||
Text(
|
||||
text = stringResource(Res.string.server_config_title),
|
||||
style = MaterialTheme.typography.headlineMedium,
|
||||
color = scheme.onSurface,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
@@ -568,4 +579,18 @@ fun ServerConfigScreen() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (preAuth) {
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.windowInsetsPadding(WindowInsets.safeDrawing.union(WindowInsets.extraStatusBars)),
|
||||
) {
|
||||
AuthContentFrame { _ ->
|
||||
ServerConfigContent()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
ServerConfigContent()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user