diff --git a/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerScreen.kt b/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerScreen.kt index 4eff5c9..cdcac28 100644 --- a/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerScreen.kt +++ b/features/messenger/src/main/kotlin/app/dapk/st/messenger/MessengerScreen.kt @@ -159,18 +159,31 @@ private fun ColumnScope.RoomContent(self: UserId, state: RoomState) { ) { index, item -> val previousEvent = if (index != 0) state.events[index - 1] else null val wasPreviousMessageSameSender = previousEvent?.author?.id == item.author.id - - when (item) { - is Message -> Message(self, item, wasPreviousMessageSameSender) - is RoomEvent.Reply -> Reply(self, item, wasPreviousMessageSameSender) - is RoomEvent.Image -> Image(self, item, wasPreviousMessageSameSender) + AlignedBubble(item, self, wasPreviousMessageSameSender) { + when (item) { + is RoomEvent.Image -> MessageImage(it as BubbleContent) + is Message -> TextBubbleContent(it as BubbleContent) + is RoomEvent.Reply -> ReplyBubbleContent(it as BubbleContent) + } } } } } +private data class BubbleContent( + val shape: RoundedCornerShape, + val background: Color, + val isNotSelf: Boolean, + val message: T +) + @Composable -private fun LazyItemScope.Image(self: UserId, message: RoomEvent.Image, wasPreviousMessageSameSender: Boolean) { +private fun LazyItemScope.AlignedBubble( + message: T, + self: UserId, + wasPreviousMessageSameSender: Boolean, + content: @Composable (BubbleContent) -> Unit +) { when (message.author.id == self) { true -> { Box(modifier = Modifier.fillParentMaxWidth(), contentAlignment = Alignment.TopEnd) { @@ -180,7 +193,7 @@ private fun LazyItemScope.Image(self: UserId, message: RoomEvent.Image, wasPrevi isNotSelf = false, wasPreviousMessageSameSender = wasPreviousMessageSameSender ) { - MessageImage(selfBackgroundShape, SmallTalkTheme.extendedColors.selfBubble, false, message) + content(BubbleContent(selfBackgroundShape, SmallTalkTheme.extendedColors.selfBubble, false, message)) } } } @@ -192,7 +205,7 @@ private fun LazyItemScope.Image(self: UserId, message: RoomEvent.Image, wasPrevi isNotSelf = true, wasPreviousMessageSameSender = wasPreviousMessageSameSender ) { - MessageImage(othersBackgroundShape, SmallTalkTheme.extendedColors.othersBubble, true, message) + content(BubbleContent(othersBackgroundShape, SmallTalkTheme.extendedColors.othersBubble, true, message)) } } } @@ -200,13 +213,13 @@ private fun LazyItemScope.Image(self: UserId, message: RoomEvent.Image, wasPrevi } @Composable -private fun MessageImage(shape: RoundedCornerShape, background: Color, isNotSelf: Boolean, message: RoomEvent.Image) { +private fun MessageImage(content: BubbleContent) { Box(modifier = Modifier.padding(start = 6.dp)) { Box( Modifier .padding(4.dp) - .clip(shape) - .background(background) + .clip(content.shape) + .background(content.background) .height(IntrinsicSize.Max), ) { Column( @@ -215,22 +228,22 @@ private fun MessageImage(shape: RoundedCornerShape, background: Color, isNotSelf .width(IntrinsicSize.Max) .defaultMinSize(minWidth = 50.dp) ) { - if (isNotSelf) { + if (content.isNotSelf) { Text( fontSize = 11.sp, - text = message.author.displayName ?: message.author.id.value, + text = content.message.author.displayName ?: content.message.author.id.value, maxLines = 1, color = MaterialTheme.colors.onPrimary ) } - val width = with(LocalDensity.current) { message.imageMeta.width.toDp() } - val height = with(LocalDensity.current) { message.imageMeta.height.toDp() } + val width = with(LocalDensity.current) { content.message.imageMeta.width.toDp() } + val height = with(LocalDensity.current) { content.message.imageMeta.height.toDp() } Spacer(modifier = Modifier.height(4.dp)) Image( modifier = Modifier.size(width, height), painter = rememberImagePainter( - data = message, + data = content.message, builder = { fetcher(DecryptingFetcher()) } ), contentDescription = null, @@ -238,15 +251,15 @@ private fun MessageImage(shape: RoundedCornerShape, background: Color, isNotSelf Spacer(modifier = Modifier.height(4.dp)) Row(horizontalArrangement = Arrangement.End, verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { - val editedPrefix = if (message.edited) "(edited) " else null + val editedPrefix = if (content.message.edited) "(edited) " else null Text( fontSize = 9.sp, - text = "${editedPrefix ?: ""}${message.time}", + text = "${editedPrefix ?: ""}${content.message.time}", textAlign = TextAlign.End, color = MaterialTheme.colors.onPrimary, modifier = Modifier.wrapContentSize() ) - SendStatus(message) + SendStatus(content.message) } } } @@ -255,67 +268,6 @@ private fun MessageImage(shape: RoundedCornerShape, background: Color, isNotSelf } -@Composable -private fun LazyItemScope.Message(self: UserId, message: Message, wasPreviousMessageSameSender: Boolean) { - when (message.author.id == self) { - true -> { - Box(modifier = Modifier.fillParentMaxWidth(), contentAlignment = Alignment.TopEnd) { - Box(modifier = Modifier.fillParentMaxWidth(0.85f), contentAlignment = Alignment.TopEnd) { - Bubble( - message = message, - isNotSelf = false, - wasPreviousMessageSameSender = wasPreviousMessageSameSender - ) { - TextBubbleContent(selfBackgroundShape, SmallTalkTheme.extendedColors.selfBubble, false, message) - } - } - } - } - false -> { - Box(modifier = Modifier.fillParentMaxWidth(0.95f), contentAlignment = Alignment.TopStart) { - Bubble( - message = message, - isNotSelf = true, - wasPreviousMessageSameSender = wasPreviousMessageSameSender - ) { - TextBubbleContent(othersBackgroundShape, SmallTalkTheme.extendedColors.othersBubble, true, message) - } - } - } - } -} - -@Composable -private fun LazyItemScope.Reply(self: UserId, message: RoomEvent.Reply, wasPreviousMessageSameSender: Boolean) { - when (message.message.author.id == self) { - true -> { - Box(modifier = Modifier.fillParentMaxWidth(), contentAlignment = Alignment.TopEnd) { - Box(modifier = Modifier.fillParentMaxWidth(0.85f), contentAlignment = Alignment.TopEnd) { - Bubble( - message = message.message, - isNotSelf = false, - wasPreviousMessageSameSender = wasPreviousMessageSameSender - ) { - ReplyBubbleContent(selfBackgroundShape, SmallTalkTheme.extendedColors.selfBubble, false, message) - } - } - } - } - false -> { - Box(modifier = Modifier.fillParentMaxWidth(0.95f), contentAlignment = Alignment.TopStart) { - Bubble( - message = message.message, - isNotSelf = true, - wasPreviousMessageSameSender = wasPreviousMessageSameSender - ) { - ReplyBubbleContent(othersBackgroundShape, SmallTalkTheme.extendedColors.othersBubble, true, message) - } - } - } - } -} - - private val selfBackgroundShape = RoundedCornerShape(12.dp, 0.dp, 12.dp, 12.dp) private val othersBackgroundShape = RoundedCornerShape(0.dp, 12.dp, 12.dp, 12.dp) @@ -348,13 +300,13 @@ private fun Bubble( } @Composable -private fun TextBubbleContent(shape: RoundedCornerShape, background: Color, isNotSelf: Boolean, message: Message) { +private fun TextBubbleContent(content: BubbleContent) { Box(modifier = Modifier.padding(start = 6.dp)) { Box( Modifier .padding(4.dp) - .clip(shape) - .background(background) + .clip(content.shape) + .background(content.background) .height(IntrinsicSize.Max), ) { Column( @@ -363,16 +315,16 @@ private fun TextBubbleContent(shape: RoundedCornerShape, background: Color, isNo .width(IntrinsicSize.Max) .defaultMinSize(minWidth = 50.dp) ) { - if (isNotSelf) { + if (content.isNotSelf) { Text( fontSize = 11.sp, - text = message.author.displayName ?: message.author.id.value, + text = content.message.author.displayName ?: content.message.author.id.value, maxLines = 1, color = MaterialTheme.colors.onPrimary ) } Text( - text = message.content, + text = content.message.content, color = MaterialTheme.colors.onPrimary, fontSize = 15.sp, modifier = Modifier.wrapContentSize(), @@ -381,15 +333,15 @@ private fun TextBubbleContent(shape: RoundedCornerShape, background: Color, isNo Spacer(modifier = Modifier.height(2.dp)) Row(horizontalArrangement = Arrangement.End, verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { - val editedPrefix = if (message.edited) "(edited) " else null + val editedPrefix = if (content.message.edited) "(edited) " else null Text( fontSize = 9.sp, - text = "${editedPrefix ?: ""}${message.time}", + text = "${editedPrefix ?: ""}${content.message.time}", textAlign = TextAlign.End, color = MaterialTheme.colors.onPrimary, modifier = Modifier.wrapContentSize() ) - SendStatus(message) + SendStatus(content.message) } } } @@ -397,13 +349,13 @@ private fun TextBubbleContent(shape: RoundedCornerShape, background: Color, isNo } @Composable -private fun ReplyBubbleContent(shape: RoundedCornerShape, background: Color, isNotSelf: Boolean, reply: RoomEvent.Reply) { +private fun ReplyBubbleContent(content: BubbleContent) { Box(modifier = Modifier.padding(start = 6.dp)) { Box( Modifier .padding(4.dp) - .clip(shape) - .background(background) + .clip(content.shape) + .background(content.background) .height(IntrinsicSize.Max), ) { Column( @@ -414,17 +366,18 @@ private fun ReplyBubbleContent(shape: RoundedCornerShape, background: Color, isN ) { Column( Modifier - .background(if (isNotSelf) SmallTalkTheme.extendedColors.otherBubbleReplyBackground else SmallTalkTheme.extendedColors.selfBubbleReplyBackground) + .background(if (content.isNotSelf) SmallTalkTheme.extendedColors.otherBubbleReplyBackground else SmallTalkTheme.extendedColors.selfBubbleReplyBackground) .padding(4.dp) ) { - val replyName = if (!isNotSelf && reply.replyingToSelf) "You" else reply.replyingTo.author.displayName ?: reply.replyingTo.author.id.value + val replyName = if (!content.isNotSelf && content.message.replyingToSelf) "You" else content.message.replyingTo.author.displayName + ?: content.message.replyingTo.author.id.value Text( fontSize = 11.sp, text = replyName, maxLines = 1, color = MaterialTheme.colors.onPrimary ) - when (val replyingTo = reply.replyingTo) { + when (val replyingTo = content.message.replyingTo) { is Message -> { Text( text = replyingTo.content, @@ -434,20 +387,34 @@ private fun ReplyBubbleContent(shape: RoundedCornerShape, background: Color, isN textAlign = TextAlign.Start, ) } + is RoomEvent.Image -> { + val width = with(LocalDensity.current) { replyingTo.imageMeta.width.toDp() } + val height = with(LocalDensity.current) { replyingTo.imageMeta.height.toDp() } + Spacer(modifier = Modifier.height(4.dp)) + Image( + modifier = Modifier.size(width, height), + painter = rememberImagePainter( + data = replyingTo, + builder = { fetcher(DecryptingFetcher()) } + ), + contentDescription = null, + ) + Spacer(modifier = Modifier.height(4.dp)) + } } } Spacer(modifier = Modifier.height(12.dp)) - if (isNotSelf) { + if (content.isNotSelf) { Text( fontSize = 11.sp, - text = reply.message.author.displayName ?: reply.message.author.id.value, + text = content.message.message.author.displayName ?: content.message.message.author.id.value, maxLines = 1, color = MaterialTheme.colors.onPrimary ) } - when (val message = reply.message) { + when (val message = content.message.message) { is Message -> { Text( text = message.content, @@ -457,18 +424,32 @@ private fun ReplyBubbleContent(shape: RoundedCornerShape, background: Color, isN textAlign = TextAlign.Start, ) } + is RoomEvent.Image -> { + val width = with(LocalDensity.current) { message.imageMeta.width.toDp() } + val height = with(LocalDensity.current) { message.imageMeta.height.toDp() } + Spacer(modifier = Modifier.height(4.dp)) + Image( + modifier = Modifier.size(width, height), + painter = rememberImagePainter( + data = content.message, + builder = { fetcher(DecryptingFetcher()) } + ), + contentDescription = null, + ) + Spacer(modifier = Modifier.height(4.dp)) + } } Spacer(modifier = Modifier.height(2.dp)) Row(horizontalArrangement = Arrangement.End, verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { Text( fontSize = 9.sp, - text = reply.time, + text = content.message.time, textAlign = TextAlign.End, color = MaterialTheme.colors.onPrimary, modifier = Modifier.wrapContentSize() ) - SendStatus(reply.message) + SendStatus(content.message.message) } } }