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 widthSizeClass = currentWindowAdaptiveInfo().widthSizeClass
|
||||||
val isDesktopListDetail = widthSizeClass != WindowWidthSizeClass.COMPACT
|
val isDesktopListDetail = widthSizeClass != WindowWidthSizeClass.COMPACT
|
||||||
var pendingMainTab by remember { mutableIntStateOf(MAIN_PAGE_CHATS) }
|
var pendingMainTab by remember { mutableIntStateOf(MAIN_PAGE_CHATS) }
|
||||||
var pendingPreAuthSettingsDetailRoute by remember { mutableStateOf<String?>(null) }
|
|
||||||
|
|
||||||
// Handle startup/deep-link navigation targets (profile links)
|
// Handle startup/deep-link navigation targets (profile links)
|
||||||
LaunchedEffect(
|
LaunchedEffect(
|
||||||
@@ -504,21 +503,19 @@ fun App(
|
|||||||
LaunchedEffect(Unit) {
|
LaunchedEffect(Unit) {
|
||||||
DesktopMenuCommands.commands.collect { command ->
|
DesktopMenuCommands.commands.collect { command ->
|
||||||
if (command != DesktopMenuCommand.OpenAbout) return@collect
|
if (command != DesktopMenuCommand.OpenAbout) return@collect
|
||||||
pendingMainTab = MAIN_PAGE_SETTINGS
|
// Logged-out: same root About as Welcome/auth overflow — never mount
|
||||||
if (isDesktopListDetail) {
|
// the empty chat list–detail shell.
|
||||||
if (ApiClient.token.isNullOrBlank()) {
|
if (ApiClient.token.isNullOrBlank()) {
|
||||||
// Pre-auth: the settings detail NavHost isn't mounted until the root
|
navController.navigate(SettingsRoutes.About) {
|
||||||
// shell is visible, so we defer settings navigation until `currentRoute`
|
|
||||||
// becomes `chat`.
|
|
||||||
pendingPreAuthSettingsDetailRoute = SettingsRoutes.About
|
|
||||||
navController.navigate("chat") {
|
|
||||||
launchSingleTop = true
|
launchSingleTop = true
|
||||||
}
|
}
|
||||||
} else {
|
return@collect
|
||||||
|
}
|
||||||
|
pendingMainTab = MAIN_PAGE_SETTINGS
|
||||||
|
if (isDesktopListDetail) {
|
||||||
settingsDetailNavController.navigateReplacingMainDetail(
|
settingsDetailNavController.navigateReplacingMainDetail(
|
||||||
route = SettingsRoutes.About,
|
route = SettingsRoutes.About,
|
||||||
)
|
)
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
navController.navigateReplacingMainDetail(
|
navController.navigateReplacingMainDetail(
|
||||||
route = SettingsRoutes.About,
|
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 {
|
ScreenSurface {
|
||||||
Box(Modifier.fillMaxSize()) {
|
Box(Modifier.fillMaxSize()) {
|
||||||
@Composable
|
@Composable
|
||||||
|
|||||||
@@ -4,36 +4,25 @@ import androidx.compose.foundation.layout.Arrangement
|
|||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.heightIn
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
import androidx.compose.foundation.layout.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.heightIn
|
||||||
import androidx.compose.foundation.layout.navigationBarsPadding
|
import androidx.compose.foundation.layout.navigationBarsPadding
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.safeDrawing
|
import androidx.compose.foundation.layout.safeDrawing
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.union
|
import androidx.compose.foundation.layout.union
|
||||||
import androidx.compose.foundation.layout.widthIn
|
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.MaterialTheme
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
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.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.Color
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
@@ -41,20 +30,14 @@ import coil3.compose.AsyncImage
|
|||||||
import org.jetbrains.compose.resources.stringResource
|
import org.jetbrains.compose.resources.stringResource
|
||||||
import ru.fromchat.Res
|
import ru.fromchat.Res
|
||||||
import ru.fromchat.api.ApiClient
|
import ru.fromchat.api.ApiClient
|
||||||
import ru.fromchat.about
|
|
||||||
import ru.fromchat.auth_get_started
|
import ru.fromchat.auth_get_started
|
||||||
import ru.fromchat.auth_welcome_tagline
|
import ru.fromchat.auth_welcome_tagline
|
||||||
import ru.fromchat.auth_welcome_title
|
import ru.fromchat.auth_welcome_title
|
||||||
import ru.fromchat.logs_title
|
import ru.fromchat.ui.auth.PreAuthOverflowMenu
|
||||||
import ru.fromchat.more
|
|
||||||
import ru.fromchat.ui.components.ActionButton
|
import ru.fromchat.ui.components.ActionButton
|
||||||
import ru.fromchat.ui.components.Text
|
import ru.fromchat.ui.components.Text
|
||||||
import ru.fromchat.ui.main.settings.SettingsRoutes
|
|
||||||
import ru.fromchat.ui.main.settings.SettingsStepHorizontalPadding
|
import ru.fromchat.ui.main.settings.SettingsStepHorizontalPadding
|
||||||
|
|
||||||
private val AUTH_CONTENT_MAX_WIDTH = 600.dp
|
|
||||||
private val AUTH_CONTENT_MAX_HEIGHT = 800.dp
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun WelcomeScreen(
|
fun WelcomeScreen(
|
||||||
onGetStarted: () -> Unit,
|
onGetStarted: () -> Unit,
|
||||||
@@ -67,70 +50,36 @@ fun WelcomeScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
|
containerColor = Color.Transparent,
|
||||||
contentWindowInsets = WindowInsets.safeDrawing.union(WindowInsets.extraStatusBars),
|
contentWindowInsets = WindowInsets.safeDrawing.union(WindowInsets.extraStatusBars),
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
val navController = LocalNavController.current
|
|
||||||
var menuExpanded by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.padding(innerPadding),
|
.padding(innerPadding),
|
||||||
) {
|
) {
|
||||||
Box(modifier = Modifier.align(Alignment.TopEnd)) {
|
Box(modifier = Modifier.align(Alignment.TopEnd)) {
|
||||||
IconButton(onClick = { menuExpanded = true }) {
|
PreAuthOverflowMenu()
|
||||||
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)
|
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
BoxWithConstraints(modifier = Modifier.fillMaxSize()) {
|
BoxWithConstraints(Modifier.fillMaxSize()) {
|
||||||
val constrainWide = maxWidth > AUTH_CONTENT_MAX_WIDTH
|
val wide = maxWidth > 600.dp
|
||||||
Box(
|
Column(
|
||||||
modifier =
|
modifier = Modifier
|
||||||
if (constrainWide) {
|
.align(if (wide) Alignment.Center else Alignment.TopCenter)
|
||||||
|
.then(
|
||||||
|
if (wide) {
|
||||||
Modifier
|
Modifier
|
||||||
.align(Alignment.Center)
|
.widthIn(max = 600.dp)
|
||||||
.widthIn(max = AUTH_CONTENT_MAX_WIDTH)
|
.heightIn(max = 800.dp)
|
||||||
.heightIn(max = AUTH_CONTENT_MAX_HEIGHT)
|
|
||||||
} else {
|
} else {
|
||||||
Modifier.fillMaxSize()
|
Modifier.fillMaxSize()
|
||||||
},
|
},
|
||||||
) {
|
)
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.navigationBarsPadding()
|
.navigationBarsPadding()
|
||||||
.padding(horizontal = SettingsStepHorizontalPadding)
|
.padding(horizontal = SettingsStepHorizontalPadding)
|
||||||
.padding(bottom = 16.dp),
|
.padding(bottom = 16.dp),
|
||||||
verticalArrangement = Arrangement.Center,
|
verticalArrangement = if (wide) Arrangement.Center else Arrangement.Center,
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
) {
|
) {
|
||||||
AsyncImage(
|
AsyncImage(
|
||||||
@@ -147,6 +96,7 @@ fun WelcomeScreen(
|
|||||||
Text(
|
Text(
|
||||||
text = stringResource(Res.string.auth_welcome_title),
|
text = stringResource(Res.string.auth_welcome_title),
|
||||||
style = MaterialTheme.typography.headlineMedium,
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
textAlign = TextAlign.Center,
|
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
|
package ru.fromchat.ui.auth
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Box
|
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.fillMaxSize
|
||||||
import androidx.compose.foundation.layout.heightIn
|
|
||||||
import androidx.compose.foundation.layout.padding
|
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.Icons
|
||||||
import androidx.compose.material.icons.filled.Storage
|
import androidx.compose.material.icons.filled.Storage
|
||||||
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.SnackbarHostState
|
import androidx.compose.material3.SnackbarHostState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.DisposableEffect
|
import androidx.compose.runtime.DisposableEffect
|
||||||
@@ -18,7 +20,6 @@ import androidx.compose.runtime.remember
|
|||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.runtime.snapshotFlow
|
import androidx.compose.runtime.snapshotFlow
|
||||||
import androidx.compose.ui.Alignment
|
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.pr0gramm3r101.utils.crypto.deriveAuthSecret
|
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.TextCta
|
||||||
import ru.fromchat.ui.components.rememberExpressiveStepFlow
|
import ru.fromchat.ui.components.rememberExpressiveStepFlow
|
||||||
import ru.fromchat.ui.components.showLoggedSnackbar
|
import ru.fromchat.ui.components.showLoggedSnackbar
|
||||||
|
import ru.fromchat.ui.extraStatusBars
|
||||||
import ru.fromchat.ui.main.settings.SettingsStepHorizontalPadding
|
import ru.fromchat.ui.main.settings.SettingsStepHorizontalPadding
|
||||||
import kotlin.time.Duration.Companion.milliseconds
|
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 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 {
|
internal suspend fun probeCurrentServer() = runCatching {
|
||||||
withTimeout(AUTH_SERVER_PROBE_TIMEOUT_MS.milliseconds) {
|
withTimeout(AUTH_SERVER_PROBE_TIMEOUT_MS.milliseconds) {
|
||||||
probeServer(Settings.readServerConfig()) is ServerProbeResult.Supported
|
probeServer(Settings.readServerConfig()) is ServerProbeResult.Supported
|
||||||
@@ -443,19 +442,13 @@ fun AuthScreen(
|
|||||||
}
|
}
|
||||||
|
|
||||||
val yandexStep = yandexParams
|
val yandexStep = yandexParams
|
||||||
BoxWithConstraints(modifier = Modifier.fillMaxSize()) {
|
// Clear title bar / system bars outside the rounded panel (same as WelcomeScreen).
|
||||||
val constrainWide = maxWidth > AUTH_CONTENT_MAX_WIDTH
|
|
||||||
Box(
|
Box(
|
||||||
modifier =
|
|
||||||
if (constrainWide) {
|
|
||||||
Modifier
|
Modifier
|
||||||
.align(Alignment.Center)
|
.fillMaxSize()
|
||||||
.widthIn(max = AUTH_CONTENT_MAX_WIDTH)
|
.windowInsetsPadding(WindowInsets.safeDrawing.union(WindowInsets.extraStatusBars)),
|
||||||
.heightIn(max = AUTH_CONTENT_MAX_HEIGHT)
|
|
||||||
} else {
|
|
||||||
Modifier.fillMaxSize()
|
|
||||||
},
|
|
||||||
) {
|
) {
|
||||||
|
AuthContentFrame { _ ->
|
||||||
ExpressiveStepFlowScaffold(
|
ExpressiveStepFlowScaffold(
|
||||||
flowState = flowState,
|
flowState = flowState,
|
||||||
pages = listOf(
|
pages = listOf(
|
||||||
@@ -566,6 +559,9 @@ fun AuthScreen(
|
|||||||
),
|
),
|
||||||
snackbarHostState = snackbarHostState,
|
snackbarHostState = snackbarHostState,
|
||||||
onBackAtFirstPage = wrappedBackToWelcome,
|
onBackAtFirstPage = wrappedBackToWelcome,
|
||||||
|
trailingTopBarActions = { PreAuthOverflowMenu() },
|
||||||
|
contentBackground = MaterialTheme.colorScheme.background,
|
||||||
|
topBarWindowInsets = WindowInsets(0, 0, 0, 0),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -343,6 +343,8 @@ fun ExpressiveStepFlowScaffold(
|
|||||||
onBackAtFirstPage: () -> Unit,
|
onBackAtFirstPage: () -> Unit,
|
||||||
trailingTopBarActions: @Composable (() -> Unit)? = null,
|
trailingTopBarActions: @Composable (() -> Unit)? = null,
|
||||||
hazeScaffold: Boolean = true,
|
hazeScaffold: Boolean = true,
|
||||||
|
contentBackground: Color = MaterialTheme.colorScheme.background,
|
||||||
|
topBarWindowInsets: WindowInsets = WindowInsets.extraStatusBars,
|
||||||
) {
|
) {
|
||||||
val pagerState = flowState.pagerState
|
val pagerState = flowState.pagerState
|
||||||
val scope = flowState.scope
|
val scope = flowState.scope
|
||||||
@@ -551,7 +553,7 @@ fun ExpressiveStepFlowScaffold(
|
|||||||
fun HazeTopBar(hazeState: HazeState) {
|
fun HazeTopBar(hazeState: HazeState) {
|
||||||
val topBarHazeStyle = HazeMaterials.thin()
|
val topBarHazeStyle = HazeMaterials.thin()
|
||||||
TopAppBar(
|
TopAppBar(
|
||||||
windowInsets = WindowInsets.extraStatusBars,
|
windowInsets = topBarWindowInsets,
|
||||||
title = {},
|
title = {},
|
||||||
navigationIcon = {
|
navigationIcon = {
|
||||||
IconButton(onClick = navigateBack) {
|
IconButton(onClick = navigateBack) {
|
||||||
@@ -585,7 +587,7 @@ fun ExpressiveStepFlowScaffold(
|
|||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.windowInsetsPadding(WindowInsets.extraStatusBars)
|
.windowInsetsPadding(topBarWindowInsets)
|
||||||
.padding(horizontal = 4.dp),
|
.padding(horizontal = 4.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
) {
|
) {
|
||||||
@@ -648,7 +650,7 @@ fun ExpressiveStepFlowScaffold(
|
|||||||
state = listState,
|
state = listState,
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(scheme.background)
|
.background(contentBackground)
|
||||||
.hazeSource(hazeState)
|
.hazeSource(hazeState)
|
||||||
.onGloballyPositioned {
|
.onGloballyPositioned {
|
||||||
listViewportBounds = it.boundsInWindow()
|
listViewportBounds = it.boundsInWindow()
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
package ru.fromchat.ui.components
|
package ru.fromchat.ui.components
|
||||||
|
|
||||||
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||||
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.RowScope
|
import androidx.compose.foundation.layout.RowScope
|
||||||
import androidx.compose.foundation.layout.WindowInsets
|
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.foundation.layout.windowInsetsPadding
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import dev.chrisbanes.haze.HazeState
|
import dev.chrisbanes.haze.HazeState
|
||||||
import dev.chrisbanes.haze.blur.HazeBlurStyle
|
import dev.chrisbanes.haze.blur.HazeBlurStyle
|
||||||
@@ -25,6 +28,7 @@ fun HazeBottomBar(
|
|||||||
hazeState: HazeState,
|
hazeState: HazeState,
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
hazeStyle: HazeBlurStyle? = null,
|
hazeStyle: HazeBlurStyle? = null,
|
||||||
|
baseColor: Color? = null,
|
||||||
content: @Composable () -> Unit,
|
content: @Composable () -> Unit,
|
||||||
) {
|
) {
|
||||||
val resolvedStyle = hazeStyle ?: HazeMaterials.thin()
|
val resolvedStyle = hazeStyle ?: HazeMaterials.thin()
|
||||||
@@ -32,6 +36,10 @@ fun HazeBottomBar(
|
|||||||
Modifier.hazeEffect(state = hazeState) {
|
Modifier.hazeEffect(state = hazeState) {
|
||||||
blurEffect {
|
blurEffect {
|
||||||
style = resolvedStyle
|
style = resolvedStyle
|
||||||
|
progressive = HazeProgressive.verticalGradient(
|
||||||
|
startIntensity = 0f,
|
||||||
|
endIntensity = 1f,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -49,9 +57,21 @@ fun HazeBottomBar(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.windowInsetsPadding(WindowInsets.ime)
|
.windowInsetsPadding(WindowInsets.ime)
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.then(effectModifier)
|
|
||||||
.then(modifier),
|
.then(modifier),
|
||||||
) {
|
) {
|
||||||
|
Box(Modifier.fillMaxWidth()) {
|
||||||
|
if (baseColor != null) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.matchParentSize()
|
||||||
|
.background(baseColor),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.matchParentSize()
|
||||||
|
.then(effectModifier),
|
||||||
|
)
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@@ -63,6 +83,7 @@ fun HazeBottomBar(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun HazeActionButton(
|
fun HazeActionButton(
|
||||||
@@ -71,12 +92,18 @@ fun HazeActionButton(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
innerModifier: Modifier = Modifier,
|
innerModifier: Modifier = Modifier,
|
||||||
hazeStyle: HazeBlurStyle? = null,
|
hazeStyle: HazeBlurStyle? = null,
|
||||||
|
baseColor: Color? = null,
|
||||||
enabled: Boolean = true,
|
enabled: Boolean = true,
|
||||||
loading: Boolean = false,
|
loading: Boolean = false,
|
||||||
interactionSource: MutableInteractionSource? = null,
|
interactionSource: MutableInteractionSource? = null,
|
||||||
content: @Composable (RowScope.() -> Unit)
|
content: @Composable (RowScope.() -> Unit)
|
||||||
) {
|
) {
|
||||||
HazeBottomBar(hazeState = hazeState, modifier = modifier, hazeStyle = hazeStyle) {
|
HazeBottomBar(
|
||||||
|
hazeState = hazeState,
|
||||||
|
modifier = modifier,
|
||||||
|
hazeStyle = hazeStyle,
|
||||||
|
baseColor = baseColor,
|
||||||
|
) {
|
||||||
ActionButton(
|
ActionButton(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
modifier = innerModifier,
|
modifier = innerModifier,
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package ru.fromchat.ui.main.settings
|
|||||||
|
|
||||||
import androidx.compose.animation.core.AnimationSpec
|
import androidx.compose.animation.core.AnimationSpec
|
||||||
import androidx.compose.animation.core.animate
|
import androidx.compose.animation.core.animate
|
||||||
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||||
@@ -32,6 +33,8 @@ import kotlinx.coroutines.launch
|
|||||||
import org.jetbrains.compose.resources.stringResource
|
import org.jetbrains.compose.resources.stringResource
|
||||||
import ru.fromchat.Res
|
import ru.fromchat.Res
|
||||||
import ru.fromchat.back
|
import ru.fromchat.back
|
||||||
|
import ru.fromchat.ui.extraStatusBars
|
||||||
|
import ru.fromchat.ui.main.LocalConversationListDetailActive
|
||||||
import ru.fromchat.ui.main.detailPaneShowBackButton
|
import ru.fromchat.ui.main.detailPaneShowBackButton
|
||||||
|
|
||||||
/** Idle gap after mouse-wheel / trackpad deltas before snapping the collapsing bar. */
|
/** 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
|
// `surfaceContainerLowest` — that token reads as a separate pane fill in
|
||||||
// two-pane against the list–detail shell.
|
// two-pane against the list–detail shell.
|
||||||
val paneColor = MaterialTheme.colorScheme.background
|
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()) {
|
if (settingsDetailUseCollapsingTopBar()) {
|
||||||
MediumTopAppBar(
|
MediumTopAppBar(
|
||||||
title = title,
|
title = title,
|
||||||
navigationIcon = navigationIcon,
|
navigationIcon = navigationIcon,
|
||||||
scrollBehavior = scrollBehavior,
|
scrollBehavior = scrollBehavior,
|
||||||
|
windowInsets = topBarWindowInsets,
|
||||||
// Unscrolled: pane. Collapsed: default scrolledContainerColor (elevation).
|
// Unscrolled: pane. Collapsed: default scrolledContainerColor (elevation).
|
||||||
colors = TopAppBarDefaults.topAppBarColors(
|
colors = TopAppBarDefaults.topAppBarColors(
|
||||||
containerColor = paneColor,
|
containerColor = paneColor,
|
||||||
@@ -100,6 +111,7 @@ fun SettingsDetailTopBar(
|
|||||||
TopAppBar(
|
TopAppBar(
|
||||||
title = title,
|
title = title,
|
||||||
navigationIcon = navigationIcon,
|
navigationIcon = navigationIcon,
|
||||||
|
windowInsets = topBarWindowInsets,
|
||||||
colors = TopAppBarDefaults.topAppBarColors(
|
colors = TopAppBarDefaults.topAppBarColors(
|
||||||
containerColor = paneColor,
|
containerColor = paneColor,
|
||||||
scrolledContainerColor = 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.navigationBars
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.foundation.layout.safeDrawing
|
||||||
import androidx.compose.foundation.layout.statusBars
|
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.LazyColumn
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
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_hint
|
||||||
import ru.fromchat.server_ip_label
|
import ru.fromchat.server_ip_label
|
||||||
import ru.fromchat.ui.LocalNavController
|
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.CtaShape
|
||||||
import ru.fromchat.ui.components.DisabledBringIntoViewSpec
|
import ru.fromchat.ui.components.DisabledBringIntoViewSpec
|
||||||
import ru.fromchat.ui.components.ExpressiveIconFrame
|
import ru.fromchat.ui.components.ExpressiveIconFrame
|
||||||
@@ -231,7 +236,12 @@ fun ServerConfigScreen() {
|
|||||||
disabledContainerColor = Color.Transparent,
|
disabledContainerColor = Color.Transparent,
|
||||||
)
|
)
|
||||||
|
|
||||||
Box(Modifier.fillMaxSize()) {
|
val preAuth = ApiClient.token.isNullOrBlank()
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ServerConfigContent() {
|
||||||
|
val listBackground = scheme.background
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
modifier = Modifier.fillMaxSize(),
|
modifier = Modifier.fillMaxSize(),
|
||||||
contentWindowInsets = WindowInsets.navigationBars,
|
contentWindowInsets = WindowInsets.navigationBars,
|
||||||
@@ -346,7 +356,7 @@ fun ServerConfigScreen() {
|
|||||||
},
|
},
|
||||||
colors = TopAppBarDefaults.topAppBarColors(
|
colors = TopAppBarDefaults.topAppBarColors(
|
||||||
containerColor = Color.Transparent,
|
containerColor = Color.Transparent,
|
||||||
scrolledContainerColor = Color.Transparent
|
scrolledContainerColor = Color.Transparent,
|
||||||
),
|
),
|
||||||
modifier = Modifier.hazeEffect(state = actionHazeState) {
|
modifier = Modifier.hazeEffect(state = actionHazeState) {
|
||||||
blurEffect {
|
blurEffect {
|
||||||
@@ -356,7 +366,7 @@ fun ServerConfigScreen() {
|
|||||||
endIntensity = 0f,
|
endIntensity = 0f,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
) { innerPadding ->
|
) { innerPadding ->
|
||||||
@@ -379,7 +389,7 @@ fun ServerConfigScreen() {
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.consumeWindowInsets(innerPadding)
|
.consumeWindowInsets(innerPadding)
|
||||||
.background(MaterialTheme.colorScheme.background)
|
.background(listBackground)
|
||||||
.hazeSource(actionHazeState)
|
.hazeSource(actionHazeState)
|
||||||
.onGloballyPositioned { listViewportBounds = it.boundsInWindow() },
|
.onGloballyPositioned { listViewportBounds = it.boundsInWindow() },
|
||||||
contentPadding = PaddingValues(bottom = bottomInsetPadding),
|
contentPadding = PaddingValues(bottom = bottomInsetPadding),
|
||||||
@@ -422,6 +432,7 @@ fun ServerConfigScreen() {
|
|||||||
Text(
|
Text(
|
||||||
text = stringResource(Res.string.server_config_title),
|
text = stringResource(Res.string.server_config_title),
|
||||||
style = MaterialTheme.typography.headlineMedium,
|
style = MaterialTheme.typography.headlineMedium,
|
||||||
|
color = scheme.onSurface,
|
||||||
modifier = Modifier.fillMaxWidth(),
|
modifier = Modifier.fillMaxWidth(),
|
||||||
textAlign = TextAlign.Center,
|
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