Message formatting

This commit is contained in:
Kgothatso Ngako
2026-06-02 01:59:00 +02:00
parent d1f0a60826
commit 29f4df66f2
8 changed files with 158 additions and 32 deletions

View File

@@ -10,10 +10,10 @@ import kotlinx.coroutines.flow.Flow
@Dao
interface ChatMessageDao {
@Query("SELECT * FROM ChatMessage WHERE chatRoomId = :chatRoomId")
@Query("SELECT * FROM ChatMessage WHERE chatRoomId = :chatRoomId ORDER BY createdAt DESC")
fun observeChatMessagesByChatRoomId(chatRoomId: String): Flow<List<LocalChatMessage>>
@Query("SELECT * FROM ChatMessage WHERE chatRoomId = :chatRoomId")
@Query("SELECT * FROM ChatMessage WHERE chatRoomId = :chatRoomId ORDER BY createdAt DESC")
fun getChatMessagesByChatRoomId(chatRoomId: String): List<LocalChatMessage>
@Upsert

View File

@@ -382,7 +382,7 @@ abstract class NostrDao(
// Save chatMessage
database.chatMessageDao().upsert(
ChatMessage(
id = giftWrapPayload.id,
giftWrapPayloadId = giftWrapPayload.id,
senderPublicKey = giftWrapPayload.publicKey,
isUserMessage = SeedManager.activePublicKey().toHex() == giftWrapPayload.publicKey,
chatRoomId = chatRoomId,

View File

@@ -22,22 +22,23 @@ import kotlin.time.Instant
ForeignKey(
entity = GiftWrapPayload::class,
parentColumns = ["id"],
childColumns = ["id"],
childColumns = ["giftWrapPayloadId"],
onDelete = ForeignKey.CASCADE,
),
],
)
data class ChatMessage(
@PrimaryKey
val id: HexKey,
@PrimaryKey(autoGenerate = true)
val id: Long = 0,
val senderPublicKey: String,
val isUserMessage: Boolean,
val chatRoomId: String,
val giftWrapPayloadId: String? = null,
val replyToEventId: String? = null,
val quotedEventId: String? = null,
val replyToMessageId: Long? = null,
val quotedMessageId: Long? = null,
val content: String,

View File

@@ -1,11 +1,16 @@
package ac.cord.auxiliary.compose.database.repository
import ac.cord.auxiliary.compose.database.AuxDatabase
import ac.cord.auxiliary.compose.database.model.ChatMessage
import ac.cord.auxiliary.compose.database.model.ChatRoom
import ac.cord.auxiliary.compose.database.model.GiftWrapPayload
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalChatMessage
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalChatRoom
import ac.cord.auxiliary.compose.repository.ChatRepository
import co.touchlab.kermit.Logger
import com.vitorpamplona.quartz.nip10Notes.TextNoteEvent
import com.vitorpamplona.quartz.nip59Giftwrap.wraps.GiftWrapEvent
import fr.acinq.lightning.utils.UUID
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.Flow
@@ -42,6 +47,21 @@ class DatabaseChatRepository(
return null
}
override suspend fun sendChatMessage(
text: String,
chatRoom: ChatRoom
) {
logger.i("sendChatMessage($text): $chatRoom")
database.chatMessageDao().upsert(
ChatMessage(
content = text,
chatRoomId = chatRoom.id,
senderPublicKey = chatRoom.userPublicKey,
isUserMessage = true
)
)
}
override fun updateChatRoomSubject(
publicKey: String,
subject: String

View File

@@ -17,6 +17,8 @@ interface ChatRepository {
suspend fun getOrCreateChatRoom(publicKey: String, defaultSubject: String? = null): LocalChatRoom?
suspend fun sendChatMessage(text: String, chatRoom: ChatRoom)
fun updateChatRoomSubject(publicKey: String, subject: String): ChatRoom?
companion object {
@@ -42,6 +44,13 @@ interface ChatRepository {
return null
}
override suspend fun sendChatMessage(
text: String,
chatRoom: ChatRoom
) {
// TODO("Not yet implemented")
}
override fun updateChatRoomSubject(publicKey: String, subject: String): ChatRoom? {
return null
}

View File

@@ -181,7 +181,9 @@ fun ChatRoomDetailScreen(
} else {
IconButton(
onClick = {
chatRoomDetailMessageListViewModel.sendMessage(
textFieldState = textFieldState
)
}
) {
Icon(

View File

@@ -1,6 +1,8 @@
package ac.cord.auxiliary.compose.ui.view.model
import ac.cord.auxiliary.compose.database.model.ChatMessage
import ac.cord.auxiliary.compose.database.model.intermdiate.LocalChatRoom
import ac.cord.auxiliary.compose.extensions.toFormattedTimeAndDateString
import ac.cord.auxiliary.compose.repository.ChatRepository
import ac.cord.auxiliary.compose.repository.NostrRepository
import ac.cord.auxiliary.compose.ui.composable.navigation.routes.ChatRoomDetailRoute
@@ -8,15 +10,27 @@ import ac.cord.auxiliary.compose.ui.composable.navigation.routes.Route
import ac.cord.auxiliary.compose.ui.view.state.ChatRoomDetailMessageListUIState
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
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.text.input.TextFieldState
import androidx.compose.foundation.text.input.clearText
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Key
import androidx.compose.material.icons.filled.KeyOff
import androidx.compose.material.icons.filled.LockClock
import androidx.compose.material.icons.filled.Signpost
import androidx.compose.material.icons.filled.VpnKey
import androidx.compose.material3.Card
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
import androidx.compose.material3.Icon
import androidx.compose.material3.LoadingIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
@@ -25,6 +39,7 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
@@ -32,10 +47,12 @@ import androidx.lifecycle.viewModelScope
import androidx.lifecycle.viewmodel.initializer
import androidx.lifecycle.viewmodel.viewModelFactory
import co.touchlab.kermit.Logger
import fr.acinq.lightning.utils.UUID
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.IO
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.launch
import kotlin.uuid.Uuid
class ChatRoomDetailMessageListViewModel(
initialChatRoomDetailMessageListUIState: ChatRoomDetailMessageListUIState,
@@ -57,7 +74,7 @@ class ChatRoomDetailMessageListViewModel(
logger.d("observeChatRoomFeed")
viewModelScope.launch(Dispatchers.IO) {
chatRepository.observeChatMessageListByChatRoomId(
localChatRoom.chatRoom.userPublicKey
localChatRoom.chatRoom.id
).distinctUntilChanged().collect { chatMessages ->
logger.d("getChatMessageListByChatRoomId: $chatMessages")
chatRoomDetailMessageListUIState = ChatRoomDetailMessageListUIState.Loaded(
@@ -145,24 +162,81 @@ class ChatRoomDetailMessageListViewModel(
items = chatRoomDetailMessageListUIState.chatMessageList,
key = { it.chatMessage.id }
) { localChatMessage ->
Card(
modifier = Modifier.fillMaxWidth(),
onClick = {
Row {
if (localChatMessage.chatMessage.isUserMessage) {
Spacer(
modifier = Modifier.width(40.dp)
)
Spacer(
modifier = Modifier.weight(1f)
)
}
) {
Column(
modifier = Modifier.padding(20.dp),
horizontalAlignment = Alignment.CenterHorizontally,
Card(
onClick = {
}
) {
Text(
text = localChatMessage.chatMessage.senderPublicKey // TODO: Get send name...
Column(
modifier = Modifier.padding(10.dp),
horizontalAlignment = if (localChatMessage.chatMessage.isUserMessage) {
Alignment.End
} else {
Alignment.Start
},
verticalArrangement = Arrangement.spacedBy(6.dp)
) {
if (localChatMessage.chatMessage.isUserMessage.not()) {
Text(
text = localChatMessage.chatMessage.senderPublicKey, // TODO: Get send name...
maxLines = 1,
overflow = TextOverflow.MiddleEllipsis,
style = MaterialTheme.typography.labelSmall
)
}
Text(
text = localChatMessage.chatMessage.content,
style = MaterialTheme.typography.bodyMedium
)
Row(
horizontalArrangement = Arrangement.spacedBy(
10.dp,
Alignment.End
),
verticalAlignment = Alignment.CenterVertically,
) {
Text(
text = localChatMessage.chatMessage.createdAt.toFormattedTimeAndDateString(),
style = MaterialTheme.typography.labelSmall
)
if (localChatMessage.chatMessage.isUserMessage) {
Icon(
Icons.Default.KeyOff,
contentDescription = "Message status"
)
}
}
}
}
if (!localChatMessage.chatMessage.isUserMessage) {
Spacer(
modifier = Modifier.weight(1f)
)
Text(
text = localChatMessage.chatMessage.content
Spacer(
modifier = Modifier.width(40.dp)
)
}
}
}
}
}
@@ -191,6 +265,21 @@ class ChatRoomDetailMessageListViewModel(
}
}
fun sendMessage(textFieldState: TextFieldState) {
if (textFieldState.text.isNotBlank()) {
val text = textFieldState.text.toString()
textFieldState.clearText()
viewModelScope.launch(Dispatchers.IO) {
chatRepository.sendChatMessage(
text = text,
chatRoom = localChatRoom.chatRoom
)
}
}
}
companion object {
const val TAG = "ChatRoomDetailMessageListViewModel"