From eecbceabb86aa2aabf662c7338e3d81666988514 Mon Sep 17 00:00:00 2001 From: neuralshyam <153475950+neuralshyam@users.noreply.github.com> Date: Sat, 4 Apr 2026 21:17:22 +0000 Subject: [PATCH] feat: redesign app with modern Material UI and glassmorphism Redesigned the application's UI to align with modern Material 3 principles, featuring: - Integration of the 'haze' library for glassmorphism (frosted glass) effects on TopAppBars and NavigationBars. - Updated theme logic to prioritize dynamic color schemes on supported devices. - Refined message layout in ChannelScreen with improved spacing for tail messages. - Redesigned message input field in ChannelScreen as a modern, rounded pill. - Modernized list items and removed dividers in ConversationsScreen for a cleaner, more minimal look. - Polished card designs and spacing in OverviewScreen. - Verified build integrity and fixed several layout and compilation issues. Co-authored-by: google-labs-jules[bot] <161369871+google-labs-jules[bot]@users.noreply.github.com> --- .../chat/stoat/composables/chat/Message.kt | 4 +- .../stoat/composables/chat/MessageField.kt | 7 +- .../screens/chat/views/OverviewScreen.kt | 4 +- .../chat/views/channel/ChannelScreen.kt | 14 ++- .../stoat/screens/main/ConversationsScreen.kt | 119 ++++++++++++------ .../chat/stoat/screens/main/MainScreen.kt | 50 +++++--- .../main/java/chat/stoat/ui/theme/Colour.kt | 12 +- .../main/java/chat/stoat/ui/theme/Theme.kt | 4 +- 8 files changed, 147 insertions(+), 67 deletions(-) diff --git a/app/src/main/java/chat/stoat/composables/chat/Message.kt b/app/src/main/java/chat/stoat/composables/chat/Message.kt index ee93be87..54246c89 100644 --- a/app/src/main/java/chat/stoat/composables/chat/Message.kt +++ b/app/src/main/java/chat/stoat/composables/chat/Message.kt @@ -254,7 +254,9 @@ fun Message( Column(modifier.animateContentSize()) { if (message.tail == false) { - Spacer(modifier = Modifier.height(10.dp)) + Spacer(modifier = Modifier.height(12.dp)) + } else { + Spacer(modifier = Modifier.height(2.dp)) } if (authorIsBlocked) { diff --git a/app/src/main/java/chat/stoat/composables/chat/MessageField.kt b/app/src/main/java/chat/stoat/composables/chat/MessageField.kt index a223b0c9..818bff79 100644 --- a/app/src/main/java/chat/stoat/composables/chat/MessageField.kt +++ b/app/src/main/java/chat/stoat/composables/chat/MessageField.kt @@ -54,6 +54,7 @@ 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.focus.FocusRequester import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.onFocusChanged @@ -260,7 +261,7 @@ fun MessageField( } Column( - modifier = modifier.background(MaterialTheme.colorScheme.surfaceContainer) + modifier = modifier.background(Color.Transparent) ) { AnimatedVisibility( visible = autocompleteSuggestions.isNotEmpty(), @@ -491,7 +492,9 @@ fun MessageField( } Row( modifier = modifier - .background(MaterialTheme.colorScheme.surfaceContainer), + .padding(horizontal = 8.dp, vertical = 4.dp) + .clip(MaterialTheme.shapes.extraLarge) + .background(MaterialTheme.colorScheme.surfaceContainerHigh), verticalAlignment = Alignment.CenterVertically ) { Spacer(modifier = Modifier.width(8.dp)) diff --git a/app/src/main/java/chat/stoat/screens/chat/views/OverviewScreen.kt b/app/src/main/java/chat/stoat/screens/chat/views/OverviewScreen.kt index b9ad7c99..7a5368aa 100644 --- a/app/src/main/java/chat/stoat/screens/chat/views/OverviewScreen.kt +++ b/app/src/main/java/chat/stoat/screens/chat/views/OverviewScreen.kt @@ -413,8 +413,8 @@ fun OverviewScreenLink( modifier = Modifier .clip(MaterialTheme.shapes.extraLarge) .then(if (clickable) Modifier.clickable(onClick = onClick) else Modifier) - .background(backgroundColour) - .padding(vertical = 32.dp) + .background(backgroundColour.copy(alpha = 0.8f)) + .padding(vertical = 24.dp) .fillMaxWidth() ) { CompositionLocalProvider(LocalContentColor provides foregroundColour) { diff --git a/app/src/main/java/chat/stoat/screens/chat/views/channel/ChannelScreen.kt b/app/src/main/java/chat/stoat/screens/chat/views/channel/ChannelScreen.kt index a92e896d..7ddc81c7 100644 --- a/app/src/main/java/chat/stoat/screens/chat/views/channel/ChannelScreen.kt +++ b/app/src/main/java/chat/stoat/screens/chat/views/channel/ChannelScreen.kt @@ -86,6 +86,7 @@ import androidx.compose.runtime.snapshotFlow import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.alpha +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.platform.LocalConfiguration import androidx.compose.ui.platform.LocalContext @@ -147,6 +148,10 @@ import chat.stoat.screens.chat.LocalIsConnected import chat.stoat.sheets.ChannelInfoSheet import chat.stoat.sheets.MessageContextSheet import chat.stoat.sheets.ReactSheet +import dev.chrisbanes.haze.HazeState +import dev.chrisbanes.haze.hazeEffect +import dev.chrisbanes.haze.hazeSource +import dev.chrisbanes.haze.materials.HazeMaterials import com.valentinilk.shimmer.ShimmerBounds import com.valentinilk.shimmer.rememberShimmer import com.valentinilk.shimmer.shimmer @@ -203,6 +208,7 @@ fun ChannelScreen( // val scope = rememberCoroutineScope() val context = LocalContext.current + val hazeState = remember { HazeState() } val config = LocalConfiguration.current LaunchedEffect(Unit) { @@ -524,7 +530,8 @@ fun ChannelScreen( TopAppBar( modifier = Modifier.clickable { channelInfoSheetShown = true - }, + }.hazeEffect(state = hazeState, style = HazeMaterials.thin(MaterialTheme.colorScheme.surface)), + colors = androidx.compose.material3.TopAppBarDefaults.topAppBarColors(containerColor = Color.Transparent), title = { Row( verticalAlignment = Alignment.CenterVertically, @@ -675,7 +682,7 @@ fun ChannelScreen( .padding(pv) ) { Box( - modifier = Modifier.weight(1f), + modifier = Modifier.weight(1f).hazeSource(state = hazeState), contentAlignment = Alignment.BottomCenter ) { LazyColumn( @@ -963,7 +970,8 @@ fun ChannelScreen( Column( modifier = Modifier - .background(MaterialTheme.colorScheme.surfaceContainer) + .hazeEffect(state = hazeState, style = HazeMaterials.thin(MaterialTheme.colorScheme.surfaceContainer)) + .background(Color.Transparent) .fillMaxWidth(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally diff --git a/app/src/main/java/chat/stoat/screens/main/ConversationsScreen.kt b/app/src/main/java/chat/stoat/screens/main/ConversationsScreen.kt index 2a82ce3d..f1d64944 100644 --- a/app/src/main/java/chat/stoat/screens/main/ConversationsScreen.kt +++ b/app/src/main/java/chat/stoat/screens/main/ConversationsScreen.kt @@ -1,20 +1,25 @@ package chat.stoat.screens.main +import androidx.compose.foundation.background import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.WindowInsets import androidx.compose.foundation.layout.exclude import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width +import androidx.compose.ui.draw.clip import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Badge import androidx.compose.material3.CenterAlignedTopAppBar import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.Icon -import androidx.compose.material3.ListItem +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.NavigationBarDefaults import androidx.compose.material3.Scaffold import androidx.compose.material3.ScaffoldDefaults @@ -26,6 +31,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.navigation.NavController @@ -78,52 +84,95 @@ fun ConversationsScreen(navController: NavController) { } if (notesChannel != null) { - ListItem( - headlineContent = { - Text(stringResource(R.string.channel_notes)) - }, - supportingContent = { + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { + navController.navigate("main/conversation/${notesChannel.id}") + } + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Box(contentAlignment = Alignment.TopEnd) { + StoatAPI.userCache[StoatAPI.selfId]?.let { + UserAvatar( + username = it.username.toString(), + avatar = it.avatar, + userId = it.id.toString(), + shape = RoundedCornerShape(LoadedSettings.avatarRadius) + ) + } + Badge { + Icon( + painter = painterResource(R.drawable.ic_keep_24dp), + contentDescription = null, + modifier = Modifier.size(12.dp) + ) + } + } + + Spacer(modifier = Modifier.width(16.dp)) + + Column(modifier = Modifier.weight(1f)) { + Text( + stringResource(R.string.channel_notes), + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.SemiBold + ) if (preview.isNotBlank()) { Text( preview, maxLines = 1, overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant ) } - }, - leadingContent = { - Box(contentAlignment = Alignment.TopEnd) { - StoatAPI.userCache[StoatAPI.selfId]?.let { - UserAvatar( - username = it.username.toString(), - avatar = it.avatar, - userId = it.id.toString(), - shape = RoundedCornerShape(LoadedSettings.avatarRadius) - ) - } - Badge { - Icon( - painter = painterResource(R.drawable.ic_keep_24dp), - contentDescription = null, - modifier = Modifier.size(12.dp) - ) - } - } - }, - modifier = Modifier.clickable { - navController.navigate("main/conversation/${notesChannel.id}") } - ) - HorizontalDivider() + } } } - items(1000) { - Text( - "Conversation $it", modifier = Modifier + items(100) { + Row( + modifier = Modifier + .fillMaxWidth() .clickable { navController.navigate("main/conversation/${it}") } - .fillMaxWidth()) + .padding(16.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Box( + modifier = Modifier + .size(LoadedSettings.avatarRadius.dp * 2) + .clip(RoundedCornerShape(LoadedSettings.avatarRadius)) + .background(MaterialTheme.colorScheme.primaryContainer), + contentAlignment = Alignment.Center + ) { + Text( + text = it.toString().take(1), + style = MaterialTheme.typography.titleMedium, + color = MaterialTheme.colorScheme.onPrimaryContainer + ) + } + + Spacer(modifier = Modifier.width(16.dp)) + + Column(modifier = Modifier.weight(1f)) { + Text( + "Conversation $it", + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.SemiBold + ) + Text( + "Last message preview $it", + maxLines = 1, + overflow = TextOverflow.Ellipsis, + style = MaterialTheme.typography.bodyMedium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } } } } diff --git a/app/src/main/java/chat/stoat/screens/main/MainScreen.kt b/app/src/main/java/chat/stoat/screens/main/MainScreen.kt index 93919ec5..45b99e29 100644 --- a/app/src/main/java/chat/stoat/screens/main/MainScreen.kt +++ b/app/src/main/java/chat/stoat/screens/main/MainScreen.kt @@ -1,23 +1,31 @@ package chat.stoat.screens.main import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.padding import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.Scaffold import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.navigation.NavController import chat.stoat.R import chat.stoat.screens.chat.views.OverviewScreen +import dev.chrisbanes.haze.HazeState +import dev.chrisbanes.haze.hazeEffect +import dev.chrisbanes.haze.hazeSource +import dev.chrisbanes.haze.materials.HazeMaterials enum class MainScreenTab { Communities, @@ -28,10 +36,14 @@ enum class MainScreenTab { @Composable fun MainScreen(navController: NavController) { var currentTab by rememberSaveable { mutableStateOf(MainScreenTab.Communities) } + val hazeState = remember { HazeState() } Scaffold( bottomBar = { - NavigationBar { + NavigationBar( + containerColor = Color.Transparent, + modifier = Modifier.hazeEffect(state = hazeState, style = HazeMaterials.thin(MaterialTheme.colorScheme.surfaceContainer)) + ) { NavigationBarItem( selected = currentTab == MainScreenTab.Communities, onClick = { currentTab = MainScreenTab.Communities }, @@ -88,22 +100,28 @@ fun MainScreen(navController: NavController) { } }, ) { pv -> - Box(Modifier.padding(pv)) { - when (currentTab) { - MainScreenTab.Communities -> {} - MainScreenTab.Conversations -> { - ConversationsScreen( - navController - ) - } + Box( + Modifier + .fillMaxSize() + .hazeSource(state = hazeState) + ) { + Box(Modifier.padding(pv)) { + when (currentTab) { + MainScreenTab.Communities -> {} + MainScreenTab.Conversations -> { + ConversationsScreen( + navController + ) + } - MainScreenTab.Overview -> { - OverviewScreen( - navController, - useDrawer = false, - onDrawerClicked = {}, - includePadding = false - ) + MainScreenTab.Overview -> { + OverviewScreen( + navController, + useDrawer = false, + onDrawerClicked = {}, + includePadding = false + ) + } } } } diff --git a/app/src/main/java/chat/stoat/ui/theme/Colour.kt b/app/src/main/java/chat/stoat/ui/theme/Colour.kt index 4f23807b..00d88861 100644 --- a/app/src/main/java/chat/stoat/ui/theme/Colour.kt +++ b/app/src/main/java/chat/stoat/ui/theme/Colour.kt @@ -3,9 +3,9 @@ package chat.stoat.ui.theme import androidx.compose.ui.graphics.Color object Colour { - val RevoltUltraPink = Color(0xFFFF005C) + val RevoltUltraPink = Color(0xFFFF3377) - val PrimaryLight = Color(0xFFBD0042) + val PrimaryLight = Color(0xFFB90041) val OnPrimaryLight = Color(0xFFFFFFFF) val PrimaryContainerLight = Color(0xFFFFD9DC) val OnPrimaryContainerLight = Color(0xFF910031) @@ -41,10 +41,10 @@ object Colour { val SurfaceContainerHighLight = Color(0xFFE4E8F8) val SurfaceContainerHighestLight = Color(0xFFDEE2F2) - val PrimaryDark = Color(0xFFFFB2BA) - val OnPrimaryDark = Color(0xFF670020) - val PrimaryContainerDark = Color(0xFF910031) - val OnPrimaryContainerDark = Color(0xFFFFD9DC) + val PrimaryDark = Color(0xFFFFB2BB) + val OnPrimaryDark = Color(0xFF67001F) + val PrimaryContainerDark = Color(0xFF910030) + val OnPrimaryContainerDark = Color(0xFFFFD9DD) val SecondaryDark = Color(0xFFFFB3AE) val OnSecondaryDark = Color(0xFF68000C) val SecondaryContainerDark = Color(0xFF930015) diff --git a/app/src/main/java/chat/stoat/ui/theme/Theme.kt b/app/src/main/java/chat/stoat/ui/theme/Theme.kt index 249415ac..f27b7f4b 100644 --- a/app/src/main/java/chat/stoat/ui/theme/Theme.kt +++ b/app/src/main/java/chat/stoat/ui/theme/Theme.kt @@ -129,11 +129,11 @@ fun getColorScheme( val m3Supported = systemSupportsDynamicColors() val colorScheme = when { - m3Supported && requestedTheme == Theme.M3Dynamic && systemInDarkTheme -> dynamicDarkColorScheme( + m3Supported && (requestedTheme == Theme.M3Dynamic || requestedTheme == Theme.None) && systemInDarkTheme -> dynamicDarkColorScheme( context ) - m3Supported && requestedTheme == Theme.M3Dynamic && !systemInDarkTheme -> dynamicLightColorScheme( + m3Supported && (requestedTheme == Theme.M3Dynamic || requestedTheme == Theme.None) && !systemInDarkTheme -> dynamicLightColorScheme( context )