mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
chat view layout (#335)
Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
This commit is contained in:
@@ -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."
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user