Improvement to chat bubbles layout

pull/3/head
Vitor Pamplona 2023-01-17 10:18:30 -05:00
rodzic 0667a822f1
commit ee9422077e
6 zmienionych plików z 210 dodań i 205 usunięć

Wyświetl plik

@ -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()

Wyświetl plik

@ -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
)
}
}

Wyświetl plik

@ -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)
}
}
}
}

Wyświetl plik

@ -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 {

Wyświetl plik

@ -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
)
}

Wyświetl plik

@ -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
)
}