feat(android): persist chat history in local Room database
parent
8658f21e2b
commit
3974c061b8
@ -0,0 +1,34 @@
|
||||
package com.onlinemsg.client.data.local
|
||||
|
||||
import android.content.Context
|
||||
import androidx.room.Database
|
||||
import androidx.room.Room
|
||||
import androidx.room.RoomDatabase
|
||||
|
||||
@Database(
|
||||
entities = [ChatMessageEntity::class],
|
||||
version = 1,
|
||||
exportSchema = false
|
||||
)
|
||||
abstract class ChatDatabase : RoomDatabase() {
|
||||
abstract fun chatMessageDao(): ChatMessageDao
|
||||
|
||||
companion object {
|
||||
private const val DB_NAME = "onlinemsg_chat.db"
|
||||
|
||||
@Volatile
|
||||
private var instance: ChatDatabase? = null
|
||||
|
||||
fun getInstance(context: Context): ChatDatabase {
|
||||
return instance ?: synchronized(this) {
|
||||
instance ?: Room.databaseBuilder(
|
||||
context.applicationContext,
|
||||
ChatDatabase::class.java,
|
||||
DB_NAME
|
||||
).build().also { db ->
|
||||
instance = db
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,50 @@
|
||||
package com.onlinemsg.client.data.local
|
||||
|
||||
import com.onlinemsg.client.ui.MessageChannel
|
||||
import com.onlinemsg.client.ui.MessageRole
|
||||
import com.onlinemsg.client.ui.UiMessage
|
||||
|
||||
class ChatHistoryRepository(private val messageDao: ChatMessageDao) {
|
||||
suspend fun loadMessages(limit: Int): List<UiMessage> {
|
||||
return messageDao.listAll()
|
||||
.asSequence()
|
||||
.mapNotNull { entity -> entity.toUiMessageOrNull() }
|
||||
.toList()
|
||||
.takeLast(limit)
|
||||
}
|
||||
|
||||
suspend fun appendMessage(message: UiMessage, limit: Int) {
|
||||
messageDao.upsert(message.toEntity())
|
||||
messageDao.trimToLatest(limit)
|
||||
}
|
||||
|
||||
suspend fun clearAll() {
|
||||
messageDao.clearAll()
|
||||
}
|
||||
}
|
||||
|
||||
private fun UiMessage.toEntity(): ChatMessageEntity {
|
||||
return ChatMessageEntity(
|
||||
id = id,
|
||||
role = role.name,
|
||||
sender = sender,
|
||||
subtitle = subtitle,
|
||||
content = content,
|
||||
channel = channel.name,
|
||||
timestampMillis = timestampMillis
|
||||
)
|
||||
}
|
||||
|
||||
private fun ChatMessageEntity.toUiMessageOrNull(): UiMessage? {
|
||||
val parsedRole = runCatching { MessageRole.valueOf(role) }.getOrNull() ?: return null
|
||||
val parsedChannel = runCatching { MessageChannel.valueOf(channel) }.getOrNull() ?: return null
|
||||
return UiMessage(
|
||||
id = id,
|
||||
role = parsedRole,
|
||||
sender = sender,
|
||||
subtitle = subtitle,
|
||||
content = content,
|
||||
channel = parsedChannel,
|
||||
timestampMillis = timestampMillis
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,31 @@
|
||||
package com.onlinemsg.client.data.local
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
|
||||
@Dao
|
||||
interface ChatMessageDao {
|
||||
@Query("SELECT * FROM chat_messages ORDER BY timestampMillis ASC")
|
||||
suspend fun listAll(): List<ChatMessageEntity>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun upsert(message: ChatMessageEntity)
|
||||
|
||||
@Query(
|
||||
"""
|
||||
DELETE FROM chat_messages
|
||||
WHERE id NOT IN (
|
||||
SELECT id
|
||||
FROM chat_messages
|
||||
ORDER BY timestampMillis DESC
|
||||
LIMIT :limit
|
||||
)
|
||||
"""
|
||||
)
|
||||
suspend fun trimToLatest(limit: Int)
|
||||
|
||||
@Query("DELETE FROM chat_messages")
|
||||
suspend fun clearAll()
|
||||
}
|
||||
@ -0,0 +1,15 @@
|
||||
package com.onlinemsg.client.data.local
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(tableName = "chat_messages")
|
||||
data class ChatMessageEntity(
|
||||
@PrimaryKey val id: String,
|
||||
val role: String,
|
||||
val sender: String,
|
||||
val subtitle: String,
|
||||
val content: String,
|
||||
val channel: String,
|
||||
val timestampMillis: Long
|
||||
)
|
||||
Loading…
Reference in New Issue