android: add message room schema and core domain models
Some checks failed
CI / test (push) Failing after 2m6s
Some checks failed
CI / test (push) Failing after 2m6s
This commit is contained in:
@@ -64,3 +64,10 @@
|
||||
- Fixed Hilt dependency cycle by separating refresh `AuthApiService` with a dedicated qualifier.
|
||||
- Added `CoroutineDispatcher` DI provider and qualifier for repositories.
|
||||
- Fixed Material3 experimental API opt-in and removed deprecated `StateFlow.distinctUntilChanged()` usage.
|
||||
|
||||
### Step 11 - Sprint A / 1) Message Room + models
|
||||
- Added message domain model (`MessageItem`) for chat screen rendering.
|
||||
- Added Room entities `messages` and `message_attachments` with chat-history indexes.
|
||||
- Added `MessageDao` with observe/pagination/upsert/delete APIs.
|
||||
- Updated `MessengerDatabase` schema to include message tables and DAO.
|
||||
- Added Hilt DI provider for `MessageDao`.
|
||||
|
||||
@@ -5,15 +5,21 @@ import androidx.room.RoomDatabase
|
||||
import ru.daemonlord.messenger.data.chat.local.dao.ChatDao
|
||||
import ru.daemonlord.messenger.data.chat.local.entity.ChatEntity
|
||||
import ru.daemonlord.messenger.data.chat.local.entity.UserShortEntity
|
||||
import ru.daemonlord.messenger.data.message.local.dao.MessageDao
|
||||
import ru.daemonlord.messenger.data.message.local.entity.MessageAttachmentEntity
|
||||
import ru.daemonlord.messenger.data.message.local.entity.MessageEntity
|
||||
|
||||
@Database(
|
||||
entities = [
|
||||
ChatEntity::class,
|
||||
UserShortEntity::class,
|
||||
MessageEntity::class,
|
||||
MessageAttachmentEntity::class,
|
||||
],
|
||||
version = 1,
|
||||
version = 2,
|
||||
exportSchema = false,
|
||||
)
|
||||
abstract class MessengerDatabase : RoomDatabase() {
|
||||
abstract fun chatDao(): ChatDao
|
||||
abstract fun messageDao(): MessageDao
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
package ru.daemonlord.messenger.data.message.local.dao
|
||||
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
import androidx.room.Query
|
||||
import androidx.room.Transaction
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import ru.daemonlord.messenger.data.message.local.entity.MessageAttachmentEntity
|
||||
import ru.daemonlord.messenger.data.message.local.entity.MessageEntity
|
||||
|
||||
@Dao
|
||||
interface MessageDao {
|
||||
|
||||
@Query(
|
||||
"""
|
||||
SELECT * FROM messages
|
||||
WHERE chat_id = :chatId
|
||||
ORDER BY created_at DESC, id DESC
|
||||
LIMIT :limit
|
||||
"""
|
||||
)
|
||||
fun observeRecentMessages(
|
||||
chatId: Long,
|
||||
limit: Int = 50,
|
||||
): Flow<List<MessageEntity>>
|
||||
|
||||
@Query(
|
||||
"""
|
||||
SELECT * FROM messages
|
||||
WHERE chat_id = :chatId
|
||||
AND id < :beforeMessageId
|
||||
ORDER BY created_at DESC, id DESC
|
||||
LIMIT :limit
|
||||
"""
|
||||
)
|
||||
suspend fun getMessagesPage(
|
||||
chatId: Long,
|
||||
beforeMessageId: Long,
|
||||
limit: Int = 50,
|
||||
): List<MessageEntity>
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun upsertMessages(messages: List<MessageEntity>)
|
||||
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
suspend fun upsertAttachments(attachments: List<MessageAttachmentEntity>)
|
||||
|
||||
@Query("DELETE FROM messages WHERE chat_id = :chatId")
|
||||
suspend fun clearChatMessages(chatId: Long)
|
||||
|
||||
@Query("DELETE FROM messages WHERE id = :messageId")
|
||||
suspend fun deleteMessage(messageId: Long)
|
||||
|
||||
@Transaction
|
||||
suspend fun clearAndReplaceMessages(
|
||||
chatId: Long,
|
||||
messages: List<MessageEntity>,
|
||||
attachments: List<MessageAttachmentEntity>,
|
||||
) {
|
||||
clearChatMessages(chatId = chatId)
|
||||
upsertMessages(messages)
|
||||
if (attachments.isNotEmpty()) {
|
||||
upsertAttachments(attachments)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package ru.daemonlord.messenger.data.message.local.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(
|
||||
tableName = "message_attachments",
|
||||
indices = [
|
||||
Index(value = ["message_id"]),
|
||||
],
|
||||
)
|
||||
data class MessageAttachmentEntity(
|
||||
@PrimaryKey
|
||||
@ColumnInfo(name = "id")
|
||||
val id: Long,
|
||||
@ColumnInfo(name = "message_id")
|
||||
val messageId: Long,
|
||||
@ColumnInfo(name = "file_url")
|
||||
val fileUrl: String,
|
||||
@ColumnInfo(name = "file_type")
|
||||
val fileType: String,
|
||||
@ColumnInfo(name = "file_size")
|
||||
val fileSize: Long,
|
||||
)
|
||||
@@ -0,0 +1,42 @@
|
||||
package ru.daemonlord.messenger.data.message.local.entity
|
||||
|
||||
import androidx.room.ColumnInfo
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity(
|
||||
tableName = "messages",
|
||||
indices = [
|
||||
Index(value = ["chat_id", "created_at"]),
|
||||
Index(value = ["chat_id", "id"]),
|
||||
Index(value = ["chat_id", "updated_at"]),
|
||||
],
|
||||
)
|
||||
data class MessageEntity(
|
||||
@PrimaryKey
|
||||
@ColumnInfo(name = "id")
|
||||
val id: Long,
|
||||
@ColumnInfo(name = "chat_id")
|
||||
val chatId: Long,
|
||||
@ColumnInfo(name = "sender_id")
|
||||
val senderId: Long,
|
||||
@ColumnInfo(name = "sender_display_name")
|
||||
val senderDisplayName: String?,
|
||||
@ColumnInfo(name = "sender_username")
|
||||
val senderUsername: String?,
|
||||
@ColumnInfo(name = "sender_avatar_url")
|
||||
val senderAvatarUrl: String?,
|
||||
@ColumnInfo(name = "reply_to_message_id")
|
||||
val replyToMessageId: Long?,
|
||||
@ColumnInfo(name = "type")
|
||||
val type: String,
|
||||
@ColumnInfo(name = "text")
|
||||
val text: String?,
|
||||
@ColumnInfo(name = "status")
|
||||
val status: String?,
|
||||
@ColumnInfo(name = "created_at")
|
||||
val createdAt: String,
|
||||
@ColumnInfo(name = "updated_at")
|
||||
val updatedAt: String?,
|
||||
)
|
||||
@@ -9,6 +9,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dagger.hilt.components.SingletonComponent
|
||||
import ru.daemonlord.messenger.data.chat.local.dao.ChatDao
|
||||
import ru.daemonlord.messenger.data.chat.local.db.MessengerDatabase
|
||||
import ru.daemonlord.messenger.data.message.local.dao.MessageDao
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Module
|
||||
@@ -31,4 +32,8 @@ object DatabaseModule {
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideChatDao(database: MessengerDatabase): ChatDao = database.chatDao()
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun provideMessageDao(database: MessengerDatabase): MessageDao = database.messageDao()
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package ru.daemonlord.messenger.domain.message.model
|
||||
|
||||
data class MessageItem(
|
||||
val id: Long,
|
||||
val chatId: Long,
|
||||
val senderId: Long,
|
||||
val senderDisplayName: String?,
|
||||
val type: String,
|
||||
val text: String?,
|
||||
val createdAt: String,
|
||||
val updatedAt: String?,
|
||||
val isOutgoing: Boolean,
|
||||
val status: String?,
|
||||
val replyToMessageId: Long?,
|
||||
)
|
||||
Reference in New Issue
Block a user