feat: refine chat bubble sizing for tablets and media
- make text bubbles size closer to their content instead of stretching too wide - reduce media and circle dimensions in chat messages, especially on tablets - hide duplicate private info card in tablet split view to avoid repeated header UI
This commit is contained in:
@@ -32,6 +32,7 @@ import androidx.compose.foundation.gestures.calculateZoom
|
||||
import androidx.compose.foundation.gestures.waitForUpOrCancellation
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@@ -650,6 +651,7 @@ private fun ChatScreen(
|
||||
val listState = rememberLazyListState()
|
||||
val scope = rememberCoroutineScope()
|
||||
val allViewerMediaItems = remember(state.messages) { buildChatViewerMediaItems(state.messages) }
|
||||
val isTabletLayout = LocalConfiguration.current.screenWidthDp >= 840
|
||||
val isChannelChat = state.chatType.equals("channel", ignoreCase = true)
|
||||
val isPrivateChat = state.chatType.equals("private", ignoreCase = true)
|
||||
val showUnknownPrivateChatBanner = isPrivateChat &&
|
||||
@@ -657,7 +659,7 @@ private fun ChatScreen(
|
||||
state.isCounterpartRelationshipResolved &&
|
||||
!state.isCounterpartContact &&
|
||||
!state.isCounterpartBlocked
|
||||
val showPrivateInfoCard = isPrivateChat && state.counterpartUserId != null
|
||||
val showPrivateInfoCard = isPrivateChat && state.counterpartUserId != null && !isTabletLayout
|
||||
val canShowMembersTab = state.chatType.equals("group", ignoreCase = true) ||
|
||||
(state.chatType.equals("channel", ignoreCase = true) && state.canManageMembers)
|
||||
val timelineItems = remember(state.messages, context) { buildChatTimelineItems(state.messages, context) }
|
||||
@@ -721,7 +723,6 @@ private fun ChatScreen(
|
||||
val forwardSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
val chatInfoSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
val attachmentSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
val isTabletLayout = LocalConfiguration.current.screenWidthDp >= 840
|
||||
val adaptiveHorizontalPadding = if (isTabletLayout) 72.dp else 0.dp
|
||||
val chatInfoEntries = remember(state.messages, context) { buildChatInfoEntries(state.messages, context) }
|
||||
val giphyApiKey = remember { BuildConfig.GIPHY_API_KEY.trim() }
|
||||
@@ -4069,6 +4070,16 @@ private fun MessageBubble(
|
||||
message.type.contains("video_note", ignoreCase = true)
|
||||
)
|
||||
val renderWithoutBubble = hasSingleStickerAttachment || hasSingleCircleAttachment
|
||||
val configuration = LocalConfiguration.current
|
||||
val isTabletLayout = configuration.screenWidthDp >= 840
|
||||
val bubbleMaxWidth = when {
|
||||
hasSingleCircleAttachment -> if (isTabletLayout) 220.dp else 190.dp
|
||||
renderAsChannelPost -> if (isTabletLayout) 480.dp else 360.dp
|
||||
else -> if (isTabletLayout) 360.dp else 300.dp
|
||||
}
|
||||
val singleMediaMaxWidth = if (isTabletLayout) 320.dp else 240.dp
|
||||
val singleMediaMaxHeight = if (isTabletLayout) 260.dp else 220.dp
|
||||
val gridMediaTileHeight = if (isTabletLayout) 96.dp else 112.dp
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
@@ -4084,16 +4095,10 @@ private fun MessageBubble(
|
||||
}
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(
|
||||
if (hasSingleStickerAttachment || hasSingleCircleAttachment) {
|
||||
if (renderAsChannelPost) 0.58f else 0.52f
|
||||
} else if (renderAsChannelPost) {
|
||||
0.94f
|
||||
} else {
|
||||
0.8f
|
||||
},
|
||||
.widthIn(
|
||||
min = if (renderWithoutBubble) 96.dp else if (renderAsChannelPost) 120.dp else 82.dp,
|
||||
max = bubbleMaxWidth,
|
||||
)
|
||||
.widthIn(min = if (renderWithoutBubble) 96.dp else if (renderAsChannelPost) 120.dp else 82.dp)
|
||||
.then(
|
||||
if (renderWithoutBubble) {
|
||||
Modifier
|
||||
@@ -4233,8 +4238,8 @@ private fun MessageBubble(
|
||||
.size(176.dp)
|
||||
} else {
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.height(188.dp)
|
||||
.widthIn(max = singleMediaMaxWidth)
|
||||
.heightIn(max = singleMediaMaxHeight)
|
||||
},
|
||||
)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
@@ -4338,8 +4343,8 @@ private fun MessageBubble(
|
||||
.size(176.dp)
|
||||
} else {
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.height(188.dp)
|
||||
.widthIn(max = singleMediaMaxWidth)
|
||||
.heightIn(max = singleMediaMaxHeight)
|
||||
},
|
||||
)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
@@ -4383,7 +4388,7 @@ private fun MessageBubble(
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.height(112.dp)
|
||||
.height(gridMediaTileHeight)
|
||||
.clip(RoundedCornerShape(10.dp))
|
||||
.let { base ->
|
||||
if (openable) base.clickable { onAttachmentImageClick(image.fileUrl) } else base
|
||||
@@ -4604,6 +4609,8 @@ private fun CircleVideoAttachmentPlayer(
|
||||
onForceToggleAudioSourceHandled: (String) -> Unit,
|
||||
onForceCycleSpeedAudioSourceHandled: (String) -> Unit,
|
||||
) {
|
||||
val isTabletLayout = LocalConfiguration.current.screenWidthDp >= 840
|
||||
val circleSize = if (isTabletLayout) 190.dp else 150.dp
|
||||
val playerState = rememberManagedMediaPlayerState(
|
||||
url = url,
|
||||
speedOptions = listOf(1f),
|
||||
@@ -4648,8 +4655,7 @@ private fun CircleVideoAttachmentPlayer(
|
||||
val progressActiveColor = MaterialTheme.colorScheme.primary
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.aspectRatio(1f)
|
||||
.size(circleSize)
|
||||
.padding(horizontal = 6.dp, vertical = 2.dp),
|
||||
contentAlignment = Alignment.Center,
|
||||
) {
|
||||
|
||||
Reference in New Issue
Block a user