182 lines
5.8 KiB
Kotlin
182 lines
5.8 KiB
Kotlin
package dev.lonami.talaria.ui.screens
|
|
|
|
import androidx.compose.foundation.layout.*
|
|
import androidx.compose.foundation.lazy.LazyColumn
|
|
import androidx.compose.foundation.lazy.LazyListState
|
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
|
import androidx.compose.foundation.text.ClickableText
|
|
import androidx.compose.foundation.text.KeyboardActions
|
|
import androidx.compose.foundation.text.KeyboardOptions
|
|
import androidx.compose.material.Button
|
|
import androidx.compose.material.Card
|
|
import androidx.compose.material.Text
|
|
import androidx.compose.material.TextField
|
|
import androidx.compose.runtime.*
|
|
import androidx.compose.ui.Modifier
|
|
import androidx.compose.ui.graphics.Color
|
|
import androidx.compose.ui.res.stringResource
|
|
import androidx.compose.ui.text.AnnotatedString
|
|
import androidx.compose.ui.text.SpanStyle
|
|
import androidx.compose.ui.text.font.FontFamily
|
|
import androidx.compose.ui.text.font.FontStyle
|
|
import androidx.compose.ui.text.font.FontWeight
|
|
import androidx.compose.ui.text.input.ImeAction
|
|
import androidx.compose.ui.text.style.TextDecoration
|
|
import androidx.compose.ui.tooling.preview.Preview
|
|
import androidx.compose.ui.unit.dp
|
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
|
import dev.lonami.talaria.R
|
|
import dev.lonami.talaria.data.MockMessageRepository
|
|
import dev.lonami.talaria.ui.state.ChatViewModel
|
|
import dev.lonami.talaria.ui.theme.TalariaTheme
|
|
import kotlinx.coroutines.launch
|
|
import uniffi.talaria.Formatting
|
|
import uniffi.talaria.Message
|
|
import uniffi.talaria.TextFormat
|
|
|
|
@Composable
|
|
fun FormattedText(
|
|
text: String,
|
|
formatting: List<TextFormat>,
|
|
onFormatClicked: (TextFormat) -> Unit,
|
|
) {
|
|
val anno = AnnotatedString(text, spanStyles = formatting.map {
|
|
AnnotatedString.Range(SpanStyle(
|
|
fontWeight = when (it.format) {
|
|
Formatting.BOLD -> FontWeight.Bold
|
|
else -> FontWeight.Normal
|
|
},
|
|
fontStyle = when (it.format) {
|
|
Formatting.ITALIC -> FontStyle.Italic
|
|
else -> FontStyle.Normal
|
|
},
|
|
fontFamily = when (it.format) {
|
|
Formatting.CODE,
|
|
Formatting.PRE,
|
|
-> FontFamily.Monospace
|
|
else -> FontFamily.Default
|
|
},
|
|
textDecoration = when (it.format) {
|
|
Formatting.UNDERLINE -> TextDecoration.Underline
|
|
Formatting.STRIKE -> TextDecoration.LineThrough
|
|
else -> TextDecoration.None
|
|
},
|
|
color = when (it.format) {
|
|
Formatting.MENTION,
|
|
Formatting.HASH_TAG,
|
|
Formatting.BOT_COMMAND,
|
|
Formatting.URL,
|
|
Formatting.EMAIL,
|
|
Formatting.TEXT_URL,
|
|
Formatting.MENTION_NAME,
|
|
Formatting.PHONE,
|
|
Formatting.CASH_TAG,
|
|
-> Color(0xff0000ff)
|
|
else -> Color.Black
|
|
}), it.offset, it.offset + it.length)
|
|
})
|
|
ClickableText(
|
|
anno,
|
|
onClick = { offset ->
|
|
anno.spanStyles.indexOfFirst {
|
|
it.start <= offset && offset <= it.end
|
|
}.takeIf { it != -1 }?.also {
|
|
onFormatClicked(formatting[it])
|
|
}
|
|
}
|
|
)
|
|
}
|
|
|
|
@Composable
|
|
fun MessageCard(message: Message, modifier: Modifier = Modifier) {
|
|
Card(
|
|
elevation = 4.dp,
|
|
modifier = modifier
|
|
.fillMaxWidth()
|
|
.padding(8.dp)
|
|
) {
|
|
Column(
|
|
modifier = Modifier
|
|
.fillMaxWidth()
|
|
.padding(8.dp)
|
|
) {
|
|
Text(message.sender, fontWeight = FontWeight.Bold)
|
|
FormattedText(message.text, message.formatting, onFormatClicked = {})
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun MessageList(messages: List<Message>, listState: LazyListState, modifier: Modifier = Modifier) {
|
|
LazyColumn(modifier = modifier, state = listState) {
|
|
items(messages.size) { MessageCard(messages[it]) }
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun MessageInputField(
|
|
messageText: String,
|
|
onMessageChanged: (String) -> Unit,
|
|
onSendMessage: () -> Unit,
|
|
modifier: Modifier = Modifier,
|
|
) {
|
|
Row(modifier = modifier) {
|
|
TextField(
|
|
messageText,
|
|
placeholder = { Text(stringResource(R.string.write_message)) },
|
|
keyboardOptions = KeyboardOptions(
|
|
imeAction = ImeAction.Send
|
|
),
|
|
keyboardActions = KeyboardActions(
|
|
onDone = { onSendMessage() }
|
|
),
|
|
modifier = Modifier.weight(1.0f),
|
|
onValueChange = onMessageChanged
|
|
)
|
|
Button(
|
|
onClick = onSendMessage
|
|
) {
|
|
Text(stringResource(R.string.send_message))
|
|
}
|
|
}
|
|
}
|
|
|
|
@Composable
|
|
fun ChatScreen(
|
|
selectedDialog: String,
|
|
modifier: Modifier = Modifier,
|
|
chatViewModel: ChatViewModel = viewModel(),
|
|
) {
|
|
val chatUiState by chatViewModel.uiState.collectAsState()
|
|
var messageText by remember { mutableStateOf("") }
|
|
val messageListState = rememberLazyListState()
|
|
val coroutineScope = rememberCoroutineScope()
|
|
|
|
Column(modifier = modifier.fillMaxSize()) {
|
|
MessageList(
|
|
chatUiState.messages,
|
|
modifier = Modifier.weight(1.0f),
|
|
listState = messageListState
|
|
)
|
|
MessageInputField(messageText, onMessageChanged = {
|
|
messageText = it
|
|
}, onSendMessage = {
|
|
chatViewModel.sendMessage(selectedDialog, messageText)
|
|
messageText = ""
|
|
coroutineScope.launch {
|
|
messageListState.animateScrollToItem(chatUiState.messages.size - 1)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
@Preview
|
|
@Composable
|
|
fun ChatPreview() {
|
|
val viewModel = remember { ChatViewModel(MockMessageRepository()).apply { loadMessages("") } }
|
|
|
|
TalariaTheme {
|
|
ChatScreen("", chatViewModel = viewModel)
|
|
}
|
|
}
|