chat view layout (#335)

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
This commit is contained in:
Efim Poberezkin
2022-02-19 17:22:52 +04:00
committed by GitHub
parent 6c4888d275
commit f57a7009a3
6 changed files with 280 additions and 60 deletions
@@ -1,8 +1,10 @@
package chat.simplex.app.model
import android.annotation.SuppressLint
import androidx.compose.runtime.*
import kotlinx.datetime.*
import kotlinx.serialization.*
import java.time.format.DateTimeFormatter
class ChatModel(val controller: ChatController) {
var currentUser = mutableStateOf<User?>(null)
@@ -438,7 +440,7 @@ class ChatItem (
val content: CIContent
) {
val id: Long get() = meta.itemId
// val timestampText: String get() = meta.timestampText
val timestampText: String get() = meta.timestampText
val isRcvNew: Boolean get() = meta.itemStatus is CIStatus.RcvNew
companion object {
@@ -484,7 +486,7 @@ class CIMeta (
val itemStatus: CIStatus,
val createdAt: Instant
) {
// val timestampText: String get() = getTimestampText(itemTs)
val timestampText: String get() = getTimestampText(itemTs)
companion object {
fun getSample(id: Long, ts: Instant, text: String, status: CIStatus = CIStatus.SndNew()): CIMeta =
@@ -498,8 +500,14 @@ class CIMeta (
}
}
// TODO
fun getTimestampText(d: Instant): String = ""
// TODO use old api?
// TODO date for older timestamps
@SuppressLint("NewApi")
fun getTimestampText(d: Instant): String {
val dt = d.toLocalDateTime(TimeZone.currentSystemDefault()).toJavaLocalDateTime()
val formatter = DateTimeFormatter.ofPattern("HH:mm")
return dt.format(formatter)
}
@Serializable
sealed class CIStatus {
@@ -1,55 +1,87 @@
package chat.simplex.app.views.chat
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.material.Icon
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import chat.simplex.app.model.*
import chat.simplex.app.ui.theme.SimpleXTheme
import chat.simplex.app.views.chat.item.ChatItemView
import chat.simplex.app.views.chatlist.ChatPreviewView
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import chat.simplex.app.views.helpers.withApi
import kotlinx.datetime.Clock
@Composable
fun ChatView(chatModel: ChatModel, nav: NavController) {
if (chatModel.chatId.value != null && chatModel.chats.count() > 0) {
val chat: Chat = chatModel.chats.first { chat -> chat.chatInfo.id == chatModel.chatId.value }
Column {
ChatInfoToolbar(chat, nav)
ChatItemsList(chatModel.chatItems)
SendMsgView(sendMessage = { msg ->
GlobalScope.launch {
withContext(Dispatchers.Main) {
// show "in progress"
val cInfo = chat.chatInfo
val newItem = chatModel.controller.apiSendMessage(
type = cInfo.chatType,
id = cInfo.apiId,
mc = MsgContent.MCText(msg)
)
// hide "in progress"
if (newItem != null) chatModel.addChatItem(cInfo, newItem.chatItem)
}
}
})
ChatLayout(chat, chatModel.chatItems, back = { nav.popBackStack() }, sendMessage = { msg ->
withApi {
// show "in progress"
val cInfo = chat.chatInfo
val newItem = chatModel.controller.apiSendMessage(
type = cInfo.chatType,
id = cInfo.apiId,
mc = MsgContent.MCText(msg)
)
// hide "in progress"
if (newItem != null) chatModel.addChatItem(cInfo, newItem.chatItem)
}
})
}
}
@Composable
fun ChatLayout(
chat: Chat, chatItems: List<ChatItem>,
back: () -> Unit,
sendMessage: (String) -> Unit
) {
Scaffold(
topBar = { ChatInfoToolbar(chat, back) },
bottomBar = { SendMsgView(sendMessage) }
) { contentPadding ->
Box(
modifier = Modifier
.padding(contentPadding)
.fillMaxWidth()
.background(MaterialTheme.colors.background)
) {
ChatItemsList(chatItems)
}
}
}
@Composable
fun ChatInfoToolbar(chat: Chat, nav: NavController) {
Row {
Button(onClick = { nav.popBackStack() }) {
Text("Back")
}
Column {
fun ChatInfoToolbar(chat: Chat, back: () -> Unit) {
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.CenterStart
) {
Icon(
Icons.Outlined.ArrowBack,
"Back",
tint = MaterialTheme.colors.primary,
modifier = Modifier
.clickable(onClick = back)
.padding(start = 16.dp)
)
Column(
Modifier
.padding(horizontal = 40.dp)
.fillMaxWidth(),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(chat.chatInfo.displayName)
Text(chat.chatInfo.fullName)
}
@@ -64,3 +96,37 @@ fun ChatItemsList(chatItems: List<ChatItem>) {
}
}
}
@Preview
@Composable
fun PreviewChatViewLayout() {
SimpleXTheme {
val chatItems = listOf(
ChatItem.getSampleData(
1, CIDirection.DirectSnd(), Clock.System.now(), "hello"
),
ChatItem.getSampleData(
2, CIDirection.DirectRcv(), Clock.System.now(), "hello"
),
ChatItem.getSampleData(
3, CIDirection.DirectSnd(), Clock.System.now(), "hello"
),
ChatItem.getSampleData(
4, CIDirection.DirectSnd(), Clock.System.now(), "hello"
),
ChatItem.getSampleData(
5, CIDirection.DirectRcv(), Clock.System.now(), "hello"
)
)
ChatLayout(
chat = Chat(
chatInfo = ChatInfo.Direct.sampleData,
chatItems = chatItems,
chatStats = Chat.ChatStats()
),
chatItems = chatItems,
back = {},
sendMessage = {}
)
}
}
@@ -1,31 +1,47 @@
package chat.simplex.app.views.chat
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.Button
import androidx.compose.material.Text
import androidx.compose.material.TextField
import androidx.compose.material.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.ArrowUpward
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import chat.simplex.app.ui.theme.SimpleXTheme
@Composable
fun SendMsgView (sendMessage: (String) -> Unit) {
fun SendMsgView(sendMessage: (String) -> Unit) {
var cmd by remember { mutableStateOf("") }
Row {
TextField(value = cmd, onValueChange = { cmd = it }, modifier = Modifier.height(60.dp))
Spacer(Modifier.height(10.dp))
Button(
onClick = {
sendMessage(cmd)
cmd = ""
},
modifier = Modifier.width(40.dp).height(60.dp),
enabled = cmd.isNotEmpty()
) {
Text("Go")
}
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.CenterEnd
) {
TextField(
value = cmd,
onValueChange = { cmd = it },
modifier = Modifier
.height(60.dp)
.fillMaxWidth()
.padding(end = 40.dp)
.background(MaterialTheme.colors.background)
)
val tint = if (cmd.isNotEmpty()) MaterialTheme.colors.primary else Color.Gray
Icon(
Icons.Outlined.ArrowUpward,
"Send Message",
tint = tint,
modifier = Modifier
.width(40.dp)
.clickable {
sendMessage(cmd)
cmd = ""
}
)
}
}
@@ -0,0 +1,29 @@
package chat.simplex.app.views.chat.item
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import chat.simplex.app.model.CIDirection
import chat.simplex.app.model.ChatItem
import kotlinx.datetime.*
@Composable
fun CIMetaView(chatItem: ChatItem) {
Text(
chatItem.timestampText,
color = Color.Gray,
style = MaterialTheme.typography.caption
)
}
@Preview
@Composable
fun PreviewCIMetaView() {
CIMetaView(
chatItem = ChatItem.getSampleData(
1, CIDirection.DirectSnd(), Clock.System.now(), "hello"
)
)
}
@@ -1,10 +1,44 @@
package chat.simplex.app.views.chat.item
import androidx.compose.material.Text
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.runtime.Composable
import chat.simplex.app.model.ChatItem
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import chat.simplex.app.model.*
import chat.simplex.app.ui.theme.SimpleXTheme
import kotlinx.datetime.Clock
@Composable
fun ChatItemView(chatItem: ChatItem) {
Text(chatItem.content.text)
val sent = chatItem.chatDir.sent
val alignment = if (sent) Alignment.CenterEnd else Alignment.CenterStart
Box(
modifier = Modifier
.padding(bottom = 4.dp)
.fillMaxWidth()
.padding(
start = if (sent) 60.dp else 16.dp,
end = if (sent) 16.dp else 60.dp,
),
contentAlignment = alignment,
) {
TextItemView(chatItem)
}
}
@Preview
@Composable
fun PreviewChatItemView() {
SimpleXTheme {
ChatItemView(
chatItem = ChatItem.getSampleData(
1, CIDirection.DirectSnd(), Clock.System.now(), "hello"
)
)
}
}
@@ -1,10 +1,77 @@
package chat.simplex.app.views.chat.item
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import chat.simplex.app.model.ChatItem
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import chat.simplex.app.model.*
import chat.simplex.app.ui.theme.SimpleXTheme
import kotlinx.datetime.*
// TODO move to theme
val SentColorLight = Color(0x1E45B8FF)
val ReceivedColorLight = Color(0x1EF1F0F5)
@Composable
fun TextItemView(chatItem: ChatItem) {
Text("TextItemView")
val sent = chatItem.chatDir.sent
val color = if (sent) SentColorLight else ReceivedColorLight
Surface(
shape = RoundedCornerShape(18.dp),
color = color
) {
Box(
modifier = Modifier.padding(vertical = 6.dp, horizontal = 12.dp)
) {
Column {
Text(text = chatItem.content.text)
CIMetaView(chatItem)
}
}
}
}
@Preview
@Composable
fun PreviewTextItemViewSnd() {
SimpleXTheme {
TextItemView(
chatItem = ChatItem.getSampleData(
1, CIDirection.DirectSnd(), Clock.System.now(), "hello"
)
)
}
}
@Preview
@Composable
fun PreviewTextItemViewRcv() {
SimpleXTheme {
TextItemView(
chatItem = ChatItem.getSampleData(
1, CIDirection.DirectRcv(), Clock.System.now(), "hello"
)
)
}
}
@Preview
@Composable
fun PreviewTextItemViewLong() {
SimpleXTheme {
TextItemView(
chatItem = ChatItem.getSampleData(
1,
CIDirection.DirectSnd(),
Clock.System.now(),
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum."
)
)
}
}