From d73976537d6df37f794063c4b6e94981ec6af96e Mon Sep 17 00:00:00 2001 From: kargathara Aakash <97916053+Kargatharaakash@users.noreply.github.com> Date: Sun, 4 Jan 2026 12:27:55 +0530 Subject: [PATCH] Fix small-screen header clipping (#519) (#532) * Fix small-screen header clipping: reserve status-bar space and center-constrain titles (fixes #519) * Remove redundant Gradle configuration lines --- .../java/com/bitchat/android/ui/ChatHeader.kt | 18 +++- .../java/com/bitchat/android/ui/ChatScreen.kt | 87 ++++++++++--------- gradle.properties | 2 +- 3 files changed, 60 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/com/bitchat/android/ui/ChatHeader.kt b/app/src/main/java/com/bitchat/android/ui/ChatHeader.kt index f741dafa..1c5c3a11 100644 --- a/app/src/main/java/com/bitchat/android/ui/ChatHeader.kt +++ b/app/src/main/java/com/bitchat/android/ui/ChatHeader.kt @@ -26,6 +26,8 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.ImeAction import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp +import androidx.compose.ui.text.style.TextOverflow +import androidx.compose.ui.text.style.TextAlign import com.bitchat.android.core.ui.utils.singleOrTripleClickable import androidx.compose.foundation.Canvas import androidx.compose.ui.geometry.Offset @@ -406,7 +408,9 @@ private fun PrivateChatHeader( Text( text = titleText, style = MaterialTheme.typography.titleMedium, - color = Color(0xFFFF9500) // Orange + color = Color(0xFFFF9500), // Orange + maxLines = 1, + overflow = TextOverflow.Ellipsis ) Spacer(modifier = Modifier.width(4.dp)) @@ -493,8 +497,12 @@ private fun ChannelHeader( style = MaterialTheme.typography.titleMedium, color = Color(0xFFFF9500), // Orange to match input field modifier = Modifier - .align(Alignment.Center) - .clickable { onSidebarClick() } + .fillMaxWidth() + .padding(horizontal = 56.dp) + .clickable { onSidebarClick() }, + textAlign = TextAlign.Center, + maxLines = 1, + overflow = TextOverflow.Ellipsis ) // Leave button - positioned on the right @@ -552,7 +560,9 @@ private fun MainHeader( modifier = Modifier.singleOrTripleClickable( onSingleClick = onTitleClick, onTripleClick = onTripleTitleClick - ) + ), + maxLines = 1, + overflow = TextOverflow.Ellipsis ) Spacer(modifier = Modifier.width(2.dp)) diff --git a/app/src/main/java/com/bitchat/android/ui/ChatScreen.kt b/app/src/main/java/com/bitchat/android/ui/ChatScreen.kt index d4d33ac0..3c2e262b 100644 --- a/app/src/main/java/com/bitchat/android/ui/ChatScreen.kt +++ b/app/src/main/java/com/bitchat/android/ui/ChatScreen.kt @@ -23,6 +23,7 @@ import androidx.compose.ui.text.TextRange import androidx.compose.ui.text.input.TextFieldValue import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.Dp + import androidx.compose.ui.zIndex import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.bitchat.android.model.BitchatMessage @@ -113,7 +114,10 @@ fun ChatScreen(viewModel: ChatViewModel) { .background(colorScheme.background) // Extend background to fill entire screen including status bar ) { val headerHeight = 42.dp - + // Reserve exact height: header + status bar inset so content below won't be overlapped + val statusBarTop = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() + val reservedHeaderHeight = headerHeight + statusBarTop + // Main content area that responds to keyboard/window insets Column( modifier = Modifier @@ -121,11 +125,10 @@ fun ChatScreen(viewModel: ChatViewModel) { .windowInsetsPadding(WindowInsets.ime) // This handles keyboard insets .windowInsetsPadding(WindowInsets.navigationBars) // Add bottom padding when keyboard is not expanded ) { - // Header spacer - creates exact space for the floating header (status bar + compact header) + // Header spacer - reserve space equal to header + status bar inset Spacer( modifier = Modifier - .windowInsetsPadding(WindowInsets.statusBars) - .height(headerHeight) + .height(reservedHeaderHeight) ) // Messages area - takes up available space, will compress when keyboard appears @@ -254,15 +257,7 @@ fun ChatScreen(viewModel: ChatViewModel) { onLocationNotesClick = { showLocationNotesSheet = true } ) - // Divider under header - positioned after status bar + header height - HorizontalDivider( - modifier = Modifier - .fillMaxWidth() - .windowInsetsPadding(WindowInsets.statusBars) - .offset(y = headerHeight) - .zIndex(1f), - color = colorScheme.outline.copy(alpha = 0.3f) - ) + val alpha by animateFloatAsState( targetValue = if (showSidebar) 0.5f else 0f, @@ -459,38 +454,46 @@ private fun ChatFloatingHeader( modifier = Modifier .fillMaxWidth() .zIndex(1f) - .windowInsetsPadding(WindowInsets.statusBars), // Extend into status bar area + .statusBarsPadding(), // Respect status bar insets color = colorScheme.background // Solid background color extending into status bar ) { - TopAppBar( - title = { - ChatHeaderContent( - selectedPrivatePeer = selectedPrivatePeer, - currentChannel = currentChannel, - nickname = nickname, - viewModel = viewModel, - onBackClick = { - when { - selectedPrivatePeer != null -> viewModel.endPrivateChat() - currentChannel != null -> viewModel.switchToChannel(null) + Column { + TopAppBar( + title = { + ChatHeaderContent( + selectedPrivatePeer = selectedPrivatePeer, + currentChannel = currentChannel, + nickname = nickname, + viewModel = viewModel, + onBackClick = { + when { + selectedPrivatePeer != null -> viewModel.endPrivateChat() + currentChannel != null -> viewModel.switchToChannel(null) + } + }, + onSidebarClick = onSidebarToggle, + onTripleClick = onPanicClear, + onShowAppInfo = onShowAppInfo, + onLocationChannelsClick = onLocationChannelsClick, + onLocationNotesClick = { + // Ensure location is loaded before showing sheet + locationManager.refreshChannels() + onLocationNotesClick() } - }, - onSidebarClick = onSidebarToggle, - onTripleClick = onPanicClear, - onShowAppInfo = onShowAppInfo, - onLocationChannelsClick = onLocationChannelsClick, - onLocationNotesClick = { - // Ensure location is loaded before showing sheet - locationManager.refreshChannels() - onLocationNotesClick() - } - ) - }, - colors = TopAppBarDefaults.topAppBarColors( - containerColor = Color.Transparent - ), - modifier = Modifier.height(headerHeight) // Ensure compact header height - ) + ) + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = Color.Transparent + ), + modifier = Modifier.heightIn(min = headerHeight) // Allow header to expand for accessibility font scales + ) + + // Divider under header - always aligned with header bottom + Divider( + modifier = Modifier.fillMaxWidth(), + color = colorScheme.outline.copy(alpha = 0.3f) + ) + } } } diff --git a/gradle.properties b/gradle.properties index 0838af96..fce5240f 100644 --- a/gradle.properties +++ b/gradle.properties @@ -26,4 +26,4 @@ android.nonTransitiveRClass=false kotlin.code.style=official # JVM heap size configuration to prevent OutOfMemoryError -org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError \ No newline at end of file +org.gradle.jvmargs=-Xmx4g -XX:MaxMetaspaceSize=512m -XX:+HeapDumpOnOutOfMemoryError