feat(chat-room): 7.3 로컬 DB 동기화 및 메시지 상태/정리 로직 구현
- ChatMessageDao에 상태 업데이트/정리 보조 쿼리 추가 - ChatRepository에 로컬 저장, 상태 업데이트, 오래된 메시지 정리 API 추가 - Activity 전송/상태 변경 시 DB 반영 및 로딩 후 정리 트리거
This commit is contained in:
@@ -44,6 +44,8 @@ class ChatRepository(
|
||||
val entities = data.messages.map { it.toEntity(roomId) }
|
||||
Single.fromCallable {
|
||||
kotlinx.coroutines.runBlocking { chatDao.insertMessages(entities) }
|
||||
// 오래된 메시지 정리(최신 200개 유지)
|
||||
kotlinx.coroutines.runBlocking { trimOldMessagesInternal(roomId, 200) }
|
||||
data
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
@@ -60,11 +62,50 @@ class ChatRepository(
|
||||
val entities = data.messages.map { it.toEntity(roomId) }
|
||||
Single.fromCallable {
|
||||
kotlinx.coroutines.runBlocking { chatDao.insertMessages(entities) }
|
||||
// 오래된 메시지 정리(최신 200개 유지)
|
||||
kotlinx.coroutines.runBlocking { trimOldMessagesInternal(roomId, 200) }
|
||||
data
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 로컬 임시 메시지 저장(SENDING 등)
|
||||
*/
|
||||
fun saveLocalMessage(roomId: Long, message: ChatMessage): Completable {
|
||||
return Completable.fromAction {
|
||||
kotlinx.coroutines.runBlocking { chatDao.insertMessage(message.toEntity(roomId)) }
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
|
||||
/**
|
||||
* localId 기반 상태 업데이트(SENDING→SENT/FAILED)
|
||||
*/
|
||||
fun updateMessageStatusByLocalId(roomId: Long, localId: String, newStatus: MessageStatus): Completable {
|
||||
return Completable.fromAction {
|
||||
// enum을 문자열로 변환하여 쿼리 파라미터로 전달
|
||||
kotlinx.coroutines.runBlocking { chatDao.updateStatusByLocalId(roomId, localId, newStatus.name) }
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
|
||||
/**
|
||||
* 오래된 메시지 정리(방별 최신 keepLatest개 유지)
|
||||
*/
|
||||
fun trimOldMessages(roomId: Long, keepLatest: Int = 200): Completable {
|
||||
return Completable.fromAction {
|
||||
kotlinx.coroutines.runBlocking { trimOldMessagesInternal(roomId, keepLatest) }
|
||||
}.subscribeOn(Schedulers.io())
|
||||
}
|
||||
|
||||
private suspend fun trimOldMessagesInternal(roomId: Long, keepLatest: Int) {
|
||||
if (keepLatest <= 0) return
|
||||
val offset = keepLatest - 1
|
||||
val cutoff = chatDao.getNthLatestCreatedAt(roomId, offset)
|
||||
if (cutoff != null) {
|
||||
chatDao.deleteOldMessages(roomId, cutoff)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 로그아웃 시 모든 로컬 메시지 삭제
|
||||
*/
|
||||
|
||||
@@ -270,6 +270,11 @@ class ChatRoomActivity : BaseActivity<ActivityChatRoomBinding>(
|
||||
isGrouped = false
|
||||
)
|
||||
appendMessage(ChatListItem.UserMessage(userMsg))
|
||||
// DB에도 임시 메시지 저장(7.3)
|
||||
compositeDisposable.add(
|
||||
chatRepository.saveLocalMessage(roomId, userMsg)
|
||||
.subscribe({ }, { /* 로컬 저장 실패는 UI 진행에 영향 주지 않음 */ })
|
||||
)
|
||||
|
||||
// 2) 타이핑 인디케이터 표시
|
||||
chatAdapter.showTypingIndicator()
|
||||
@@ -301,6 +306,14 @@ class ChatRoomActivity : BaseActivity<ActivityChatRoomBinding>(
|
||||
items[index] = ChatListItem.UserMessage(updated)
|
||||
chatAdapter.setItems(items)
|
||||
autoScrollIfAtBottom()
|
||||
|
||||
// DB에도 상태 업데이트 반영(7.3)
|
||||
updated.localId?.let { lid ->
|
||||
compositeDisposable.add(
|
||||
chatRepository.updateMessageStatusByLocalId(roomId, lid, newStatus)
|
||||
.subscribe({ }, { /* 실패 시 무시(다음 동기화 시 정합성 확보) */ })
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -385,6 +398,12 @@ class ChatRoomActivity : BaseActivity<ActivityChatRoomBinding>(
|
||||
// 최신 메시지 위치로
|
||||
scrollToBottom()
|
||||
isLoading = false
|
||||
|
||||
// 7.3: 오래된 메시지 정리(백그라운드)
|
||||
compositeDisposable.add(
|
||||
chatRepository.trimOldMessages(roomId, keepLatest = 200)
|
||||
.subscribe({ }, { /* 무시 */ })
|
||||
)
|
||||
}, { error ->
|
||||
isLoading = false
|
||||
showToast(error.message ?: "채팅방 데이터를 불러오지 못했습니다.")
|
||||
@@ -489,6 +508,12 @@ class ChatRoomActivity : BaseActivity<ActivityChatRoomBinding>(
|
||||
}
|
||||
|
||||
isLoading = false
|
||||
|
||||
// 7.3: 오래된 메시지 정리(백그라운드)
|
||||
compositeDisposable.add(
|
||||
chatRepository.trimOldMessages(roomId, keepLatest = 200)
|
||||
.subscribe({ }, { /* 무시 */ })
|
||||
)
|
||||
}, { error ->
|
||||
isLoading = false
|
||||
showToast(error.message ?: "이전 메시지를 불러오지 못했습니다.")
|
||||
|
||||
@@ -21,6 +21,14 @@ interface ChatMessageDao {
|
||||
@Update
|
||||
suspend fun updateMessage(message: ChatMessageEntity)
|
||||
|
||||
// 메시지 상태를 localId 기준으로 업데이트(Converter로 문자열 상태 저장)
|
||||
@Query("UPDATE chat_messages SET status = :status WHERE roomId = :roomId AND localId = :localId")
|
||||
suspend fun updateStatusByLocalId(roomId: Long, localId: String, status: String): Int
|
||||
|
||||
// N번째 최신(createdAt DESC) 항목의 createdAt (OFFSET 사용)
|
||||
@Query("SELECT createdAt FROM chat_messages WHERE roomId = :roomId ORDER BY createdAt DESC LIMIT 1 OFFSET :offset")
|
||||
suspend fun getNthLatestCreatedAt(roomId: Long, offset: Int): Long?
|
||||
|
||||
@Query("DELETE FROM chat_messages WHERE roomId = :roomId AND createdAt < :cutoffTime")
|
||||
suspend fun deleteOldMessages(roomId: Long, cutoffTime: Long)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user