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