diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt index e81f31498..f4816df5a 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/actions/NewPostView.kt @@ -199,8 +199,9 @@ fun CloseButton(onCancel: () -> Unit) { } @Composable -fun PostButton(onPost: () -> Unit = {}, isActive: Boolean) { +fun PostButton(onPost: () -> Unit = {}, isActive: Boolean, modifier: Modifier = Modifier) { Button( + modifier = modifier, onClick = { if (isActive) { onPost() diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomCompose.kt index 3b258004e..c46eaa3f7 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomCompose.kt @@ -1,6 +1,7 @@ package com.vitorpamplona.amethyst.ui.note import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.height @@ -17,10 +18,12 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp import androidx.navigation.NavController import coil.compose.AsyncImage import com.vitorpamplona.amethyst.model.Note +import com.vitorpamplona.amethyst.model.User import com.vitorpamplona.amethyst.ui.components.RichTextViewer import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel import nostr.postr.events.TextNoteEvent @@ -42,52 +45,18 @@ fun ChatroomCompose(baseNote: Note, accountViewModel: AccountViewModel, navContr val channelState by note.channel!!.live.observeAsState() val channel = channelState?.channel - Column(modifier = - Modifier.clickable( - onClick = { navController.navigate("Channel/${channel?.idHex}") } - ) - ) { - Row( - modifier = Modifier - .padding( - start = 12.dp, - end = 12.dp, - top = 10.dp) - ) { - - AsyncImage( - model = channel?.profilePicture(), - contentDescription = "Public Channel Image", - modifier = Modifier - .width(55.dp).height(55.dp) - .clip(shape = CircleShape) - ) - - Column(modifier = Modifier.padding(start = 10.dp)) { - Row(verticalAlignment = Alignment.CenterVertically) { - Text( - "${channel?.info?.name}", - fontWeight = FontWeight.Bold, - ) - - Text( - timeAgo(note.event?.createdAt), - color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f) - ) - } - - val eventContent = accountViewModel.decrypt(note) - if (eventContent != null) - RichTextViewer("${author?.toBestDisplayName()}: " + eventContent.take(100), note.event?.tags, note, accountViewModel, navController) - else - RichTextViewer("Referenced event not found", note.event?.tags, note, accountViewModel, navController) - } - } - - Divider( - modifier = Modifier.padding(top = 10.dp), - thickness = 0.25.dp - ) + channel?.let { + ChannelName( + channelPicture = it.profilePicture(), + channelTitle = { + Text( + "${it.info.name}", + fontWeight = FontWeight.Bold + ) + }, + channelLastTime = note.event?.createdAt, + channelLastContent = "${author?.toBestDisplayName()}: " + note.event?.content, + onClick = { navController.navigate("Channel/${it.idHex}") }) } } else { @@ -107,51 +76,74 @@ fun ChatroomCompose(baseNote: Note, accountViewModel: AccountViewModel, navContr } } - Column(modifier = - Modifier.clickable( - onClick = { navController.navigate("Room/${userToComposeOn?.pubkeyHex}") } - ) - ) { - Row( - modifier = Modifier - .padding( - start = 12.dp, - end = 12.dp, - top = 10.dp) - ) { - - AsyncImage( - model = userToComposeOn?.profilePicture(), - contentDescription = "Profile Image", - modifier = Modifier - .width(55.dp).height(55.dp) - .clip(shape = CircleShape) - ) - - Column(modifier = Modifier.padding(start = 10.dp)) { - Row(verticalAlignment = Alignment.CenterVertically) { - if (userToComposeOn != null) - UserDisplay(userToComposeOn) - - Text( - timeAgo(note.event?.createdAt), - color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f) - ) - } - - val eventContent = accountViewModel.decrypt(note) - if (eventContent != null) - RichTextViewer(eventContent.take(100), note.event?.tags, note, accountViewModel, navController) - else - RichTextViewer("Referenced event not found", note.event?.tags, note, accountViewModel, navController) - } - } - - Divider( - modifier = Modifier.padding(top = 10.dp), - thickness = 0.25.dp - ) + userToComposeOn?.let { + ChannelName( + channelPicture = it.profilePicture(), + channelTitle = { UserDisplay(it) }, + channelLastTime = note.event?.createdAt, + channelLastContent = accountViewModel.decrypt(note), + onClick = { navController.navigate("Room/${it.pubkeyHex}") }) } } +} + +@Composable +private fun ChannelName( + channelPicture: String, + channelTitle: @Composable () -> Unit, + channelLastTime: Long?, + channelLastContent: String?, + onClick: () -> Unit +) { + Column(modifier = Modifier.clickable(onClick = onClick) ) { + Row( + modifier = Modifier.padding(start = 12.dp, end = 12.dp, top = 10.dp) + ) { + + AsyncImage( + model = channelPicture, + contentDescription = "Profile Image", + modifier = Modifier + .width(55.dp) + .height(55.dp) + .clip(shape = CircleShape) + ) + + Column(modifier = Modifier.padding(start = 10.dp), + verticalArrangement = Arrangement.SpaceAround) { + Row( + verticalAlignment = Alignment.CenterVertically, + modifier = Modifier.padding(bottom = 4.dp) + ) { + channelTitle() + + Text( + timeAgo(channelLastTime), + color = MaterialTheme.colors.onSurface.copy(alpha = 0.52f) + ) + } + + if (channelLastContent != null) + Text( + channelLastContent, + color = MaterialTheme.colors.onSurface.copy(alpha = 0.52f), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + else + Text( + "Referenced event not found", + color = MaterialTheme.colors.onSurface.copy(alpha = 0.52f), + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } + + Divider( + modifier = Modifier.padding(top = 10.dp), + thickness = 0.25.dp + ) + } } \ No newline at end of file diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomMessageCompose.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomMessageCompose.kt index 238f14e86..cb7bfa911 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomMessageCompose.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/ChatroomMessageCompose.kt @@ -6,6 +6,7 @@ import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding @@ -30,6 +31,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Shape import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.navigation.NavController import coil.compose.AsyncImage import com.vitorpamplona.amethyst.model.Note @@ -37,8 +39,8 @@ import com.vitorpamplona.amethyst.service.model.ChannelMessageEvent import com.vitorpamplona.amethyst.ui.components.RichTextViewer import com.vitorpamplona.amethyst.ui.screen.loggedIn.AccountViewModel -val ChatBubbleShapeMe = RoundedCornerShape(20.dp, 20.dp, 3.dp, 20.dp) -val ChatBubbleShapeThem = RoundedCornerShape(20.dp, 20.dp, 20.dp, 3.dp) +val ChatBubbleShapeMe = RoundedCornerShape(15.dp, 15.dp, 3.dp, 15.dp) +val ChatBubbleShapeThem = RoundedCornerShape(3.dp, 15.dp, 15.dp, 15.dp) @OptIn(ExperimentalFoundationApi::class) @Composable @@ -57,56 +59,60 @@ fun ChatroomMessageCompose(baseNote: Note, accountViewModel: AccountViewModel, n val authorState by note.author!!.live.observeAsState() val author = authorState?.user + var backgroundBubbleColor: Color + var alignment: Arrangement.Horizontal + var shape: Shape + + if (author == accountUser) { + backgroundBubbleColor = MaterialTheme.colors.primary.copy(alpha = 0.32f) + alignment = Arrangement.End + shape = ChatBubbleShapeMe + } else { + backgroundBubbleColor = MaterialTheme.colors.onSurface.copy(alpha = 0.12f) + alignment = Arrangement.Start + shape = ChatBubbleShapeThem + } + Column() { - var backgroundBubbleColor: Color - var alignment: Arrangement.Horizontal - var shape: Shape - - if (author == accountUser) { - backgroundBubbleColor = MaterialTheme.colors.primary.copy(alpha = 0.32f) - alignment = Arrangement.End - shape = ChatBubbleShapeMe - } else { - backgroundBubbleColor = MaterialTheme.colors.onSurface.copy(alpha = 0.12f) - alignment = Arrangement.Start - shape = ChatBubbleShapeThem - } - Row( - horizontalArrangement = alignment, - modifier = Modifier.fillMaxWidth() - .padding( - start = 12.dp, - end = 12.dp, - top = 5.dp, - bottom = 5.dp - ).combinedClickable( - onClick = { }, - onLongClick = { popupExpanded = true } - ) + modifier = Modifier.fillMaxWidth(1f).padding( + start = 12.dp, + end = 12.dp, + top = 5.dp, + bottom = 5.dp + ), + horizontalArrangement = alignment ) { - Row( - verticalAlignment = Alignment.CenterVertically + horizontalArrangement = alignment, + modifier = Modifier.fillMaxWidth(0.85f) ) { + Surface( color = backgroundBubbleColor, - shape = shape + shape = shape, + modifier = Modifier + .combinedClickable( + onClick = { }, + onLongClick = { popupExpanded = true } + ) ) { Column( - modifier = Modifier.padding(10.dp), + modifier = Modifier.padding(start = 10.dp, end = 10.dp, bottom = 5.dp), ) { if (author != accountUser && note.event is ChannelMessageEvent) { Row( verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = alignment + horizontalArrangement = alignment, + modifier = Modifier.padding(top = 5.dp) ) { AsyncImage( model = author?.profilePicture(), contentDescription = "Profile Image", modifier = Modifier - .width(25.dp).height(25.dp) + .width(25.dp) + .height(25.dp) .clip(shape = CircleShape) .clickable(onClick = { author?.let { @@ -127,9 +133,7 @@ fun ChatroomMessageCompose(baseNote: Note, accountViewModel: AccountViewModel, n } } - Row( - verticalAlignment = Alignment.CenterVertically - ) { + Row(verticalAlignment = Alignment.CenterVertically) { val eventContent = accountViewModel.decrypt(note) if (eventContent != null) @@ -148,24 +152,26 @@ fun ChatroomMessageCompose(baseNote: Note, accountViewModel: AccountViewModel, n accountViewModel, navController ) - } Row( verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = alignment + horizontalArrangement = Arrangement.End, + modifier = Modifier.padding(top = 2.dp) ) { Text( timeAgoLong(note.event?.createdAt), - color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f) + color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f), + fontSize = 12.sp ) } } } - } - } - NoteDropDownMenu(note, popupExpanded, { popupExpanded = false }, accountViewModel) + } + + NoteDropDownMenu(note, popupExpanded, { popupExpanded = false }, accountViewModel) + } } } } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/TimeAgoFormatter.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/TimeAgoFormatter.kt index 34762911b..6e80b29ec 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/note/TimeAgoFormatter.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/note/TimeAgoFormatter.kt @@ -18,7 +18,7 @@ fun timeAgo(mills: Long?): String { return " • " + humanReadable .replace(" hr. ago", "h") .replace(" min. ago", "m") - .replace(" days. ago", "d") + .replace(" days ago", "d") } fun timeAgoLong(mills: Long?): String { diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt index 6d780d6f1..124dd2c25 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChannelScreen.kt @@ -15,6 +15,7 @@ import androidx.compose.material.Divider import androidx.compose.material.MaterialTheme import androidx.compose.material.OutlinedTextField import androidx.compose.material.Text +import androidx.compose.material.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState @@ -26,7 +27,9 @@ import androidx.compose.ui.draw.clip import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.input.KeyboardCapitalization import androidx.compose.ui.text.input.TextFieldValue +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp import androidx.lifecycle.viewmodel.compose.viewModel import androidx.navigation.NavController import coil.compose.AsyncImage @@ -54,13 +57,11 @@ fun ChannelScreen(channelId: String?, accountViewModel: AccountViewModel, navCon val feedViewModel: FeedViewModel = viewModel { FeedViewModel( NostrChannelDataSource ) } Column(Modifier.fillMaxHeight()) { - channel?.let { - ChannelHeader( - it, - accountViewModel = accountViewModel, - navController = navController - ) - } + ChannelHeader( + channel, + accountViewModel = accountViewModel, + navController = navController + ) Column( modifier = Modifier.fillMaxHeight().padding(vertical = 0.dp).weight(1f, true) @@ -73,7 +74,7 @@ fun ChannelScreen(channelId: String?, accountViewModel: AccountViewModel, navCon horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { - OutlinedTextField( + TextField( value = newPost.value, onValueChange = { newPost.value = it }, keyboardOptions = KeyboardOptions.Default.copy( @@ -85,60 +86,63 @@ fun ChannelScreen(channelId: String?, accountViewModel: AccountViewModel, navCon text = "reply here.. ", color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f) ) - } - ) - - PostButton( - onPost = { - account.sendChannelMeesage(newPost.value.text, channel.idHex) - newPost.value = TextFieldValue("") }, - newPost.value.text.isNotBlank() + trailingIcon = { + PostButton( + onPost = { + account.sendChannelMeesage(newPost.value.text, channel.idHex) + newPost.value = TextFieldValue("") + }, + newPost.value.text.isNotBlank(), + modifier = Modifier.padding(end = 10.dp) + ) + } ) } } } } - @Composable fun ChannelHeader(baseChannel: Channel, accountViewModel: AccountViewModel, navController: NavController) { val channelState by baseChannel.live.observeAsState() val channel = channelState?.channel - Column(modifier = - Modifier - .padding(12.dp) - ) { - Row(verticalAlignment = Alignment.CenterVertically) { + Column() { + Column(modifier = Modifier.padding(12.dp)) { + Row(verticalAlignment = Alignment.CenterVertically) { - AsyncImage( - model = channel?.profilePicture(), - contentDescription = "Profile Image", - modifier = Modifier - .width(35.dp).height(35.dp) - .clip(shape = CircleShape) - ) + AsyncImage( + model = channel?.profilePicture(), + contentDescription = "Profile Image", + modifier = Modifier + .width(35.dp).height(35.dp) + .clip(shape = CircleShape) + ) - Column(modifier = Modifier.padding(start = 10.dp)) { - Row(verticalAlignment = Alignment.CenterVertically) { - Text( - "${channel?.info?.name}", - fontWeight = FontWeight.Bold, - ) - } + Column(modifier = Modifier.padding(start = 10.dp)) { + Row(verticalAlignment = Alignment.CenterVertically) { + Text( + "${channel?.info?.name}", + fontWeight = FontWeight.Bold, + ) + } - Row(verticalAlignment = Alignment.CenterVertically) { - Text( - "${channel?.info?.about}", - color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f) - ) + Row(verticalAlignment = Alignment.CenterVertically) { + Text( + "${channel?.info?.about}", + color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f), + maxLines = 1, + overflow = TextOverflow.Ellipsis, + fontSize = 12.sp + ) + } } } } Divider( - modifier = Modifier.padding(top = 12.dp, start = 12.dp, end = 12.dp), + modifier = Modifier.padding(start = 12.dp, end = 12.dp), thickness = 0.25.dp ) } diff --git a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomScreen.kt b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomScreen.kt index a7d9d842a..fae7afcb6 100644 --- a/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomScreen.kt +++ b/app/src/main/java/com/vitorpamplona/amethyst/ui/screen/loggedIn/ChatroomScreen.kt @@ -15,6 +15,7 @@ import androidx.compose.material.Divider import androidx.compose.material.MaterialTheme import androidx.compose.material.OutlinedTextField import androidx.compose.material.Text +import androidx.compose.material.TextField import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState @@ -67,27 +68,29 @@ fun ChatroomScreen(userId: String?, accountViewModel: AccountViewModel, navContr horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { - OutlinedTextField( + TextField( value = newPost.value, onValueChange = { newPost.value = it }, keyboardOptions = KeyboardOptions.Default.copy( capitalization = KeyboardCapitalization.Sentences ), - modifier = Modifier.weight(1f, true).padding(end = 10.dp), + modifier = Modifier.weight(1f, true), placeholder = { Text( text = "reply here.. ", color = MaterialTheme.colors.onSurface.copy(alpha = 0.32f) ) - } - ) - - PostButton( - onPost = { - account.sendPrivateMeesage(newPost.value.text, userId) - newPost.value = TextFieldValue("") }, - newPost.value.text.isNotBlank() + trailingIcon = { + PostButton( + onPost = { + account.sendPrivateMeesage(newPost.value.text, userId) + newPost.value = TextFieldValue("") + }, + newPost.value.text.isNotBlank(), + modifier = Modifier.padding(end = 10.dp) + ) + } ) } } @@ -100,33 +103,32 @@ fun ChatroomHeader(baseUser: User, accountViewModel: AccountViewModel, navContro val authorState by baseUser.live.observeAsState() val author = authorState?.user - Column(modifier = - Modifier - .padding(12.dp) - .clickable( - onClick = { navController.navigate("User/${author?.pubkeyHex}") } - ) + Column(modifier = Modifier.clickable( + onClick = { navController.navigate("User/${author?.pubkeyHex}") } + ) ) { - Row(verticalAlignment = Alignment.CenterVertically) { + Column(modifier = Modifier.padding(12.dp)) { + Row(verticalAlignment = Alignment.CenterVertically) { - AsyncImage( - model = author?.profilePicture(), - contentDescription = "Profile Image", - modifier = Modifier - .width(35.dp).height(35.dp) - .clip(shape = CircleShape) - ) + AsyncImage( + model = author?.profilePicture(), + contentDescription = "Profile Image", + modifier = Modifier + .width(35.dp).height(35.dp) + .clip(shape = CircleShape) + ) - Column(modifier = Modifier.padding(start = 10.dp)) { - Row(verticalAlignment = Alignment.CenterVertically) { - if (author != null) - UserDisplay(author) + Column(modifier = Modifier.padding(start = 10.dp)) { + Row(verticalAlignment = Alignment.CenterVertically) { + if (author != null) + UserDisplay(author) + } } } } Divider( - modifier = Modifier.padding(top = 12.dp, start = 12.dp, end = 12.dp), + modifier = Modifier.padding(start = 12.dp, end = 12.dp), thickness = 0.25.dp ) }