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) }
|
val entities = data.messages.map { it.toEntity(roomId) }
|
||||||
Single.fromCallable {
|
Single.fromCallable {
|
||||||
kotlinx.coroutines.runBlocking { chatDao.insertMessages(entities) }
|
kotlinx.coroutines.runBlocking { chatDao.insertMessages(entities) }
|
||||||
|
// 오래된 메시지 정리(최신 200개 유지)
|
||||||
|
kotlinx.coroutines.runBlocking { trimOldMessagesInternal(roomId, 200) }
|
||||||
data
|
data
|
||||||
}.subscribeOn(Schedulers.io())
|
}.subscribeOn(Schedulers.io())
|
||||||
}
|
}
|
||||||
@@ -60,11 +62,50 @@ class ChatRepository(
|
|||||||
val entities = data.messages.map { it.toEntity(roomId) }
|
val entities = data.messages.map { it.toEntity(roomId) }
|
||||||
Single.fromCallable {
|
Single.fromCallable {
|
||||||
kotlinx.coroutines.runBlocking { chatDao.insertMessages(entities) }
|
kotlinx.coroutines.runBlocking { chatDao.insertMessages(entities) }
|
||||||
|
// 오래된 메시지 정리(최신 200개 유지)
|
||||||
|
kotlinx.coroutines.runBlocking { trimOldMessagesInternal(roomId, 200) }
|
||||||
data
|
data
|
||||||
}.subscribeOn(Schedulers.io())
|
}.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
|
isGrouped = false
|
||||||
)
|
)
|
||||||
appendMessage(ChatListItem.UserMessage(userMsg))
|
appendMessage(ChatListItem.UserMessage(userMsg))
|
||||||
|
// DB에도 임시 메시지 저장(7.3)
|
||||||
|
compositeDisposable.add(
|
||||||
|
chatRepository.saveLocalMessage(roomId, userMsg)
|
||||||
|
.subscribe({ }, { /* 로컬 저장 실패는 UI 진행에 영향 주지 않음 */ })
|
||||||
|
)
|
||||||
|
|
||||||
// 2) 타이핑 인디케이터 표시
|
// 2) 타이핑 인디케이터 표시
|
||||||
chatAdapter.showTypingIndicator()
|
chatAdapter.showTypingIndicator()
|
||||||
@@ -301,6 +306,14 @@ class ChatRoomActivity : BaseActivity<ActivityChatRoomBinding>(
|
|||||||
items[index] = ChatListItem.UserMessage(updated)
|
items[index] = ChatListItem.UserMessage(updated)
|
||||||
chatAdapter.setItems(items)
|
chatAdapter.setItems(items)
|
||||||
autoScrollIfAtBottom()
|
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()
|
scrollToBottom()
|
||||||
isLoading = false
|
isLoading = false
|
||||||
|
|
||||||
|
// 7.3: 오래된 메시지 정리(백그라운드)
|
||||||
|
compositeDisposable.add(
|
||||||
|
chatRepository.trimOldMessages(roomId, keepLatest = 200)
|
||||||
|
.subscribe({ }, { /* 무시 */ })
|
||||||
|
)
|
||||||
}, { error ->
|
}, { error ->
|
||||||
isLoading = false
|
isLoading = false
|
||||||
showToast(error.message ?: "채팅방 데이터를 불러오지 못했습니다.")
|
showToast(error.message ?: "채팅방 데이터를 불러오지 못했습니다.")
|
||||||
@@ -489,6 +508,12 @@ class ChatRoomActivity : BaseActivity<ActivityChatRoomBinding>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
isLoading = false
|
isLoading = false
|
||||||
|
|
||||||
|
// 7.3: 오래된 메시지 정리(백그라운드)
|
||||||
|
compositeDisposable.add(
|
||||||
|
chatRepository.trimOldMessages(roomId, keepLatest = 200)
|
||||||
|
.subscribe({ }, { /* 무시 */ })
|
||||||
|
)
|
||||||
}, { error ->
|
}, { error ->
|
||||||
isLoading = false
|
isLoading = false
|
||||||
showToast(error.message ?: "이전 메시지를 불러오지 못했습니다.")
|
showToast(error.message ?: "이전 메시지를 불러오지 못했습니다.")
|
||||||
|
|||||||
@@ -21,6 +21,14 @@ interface ChatMessageDao {
|
|||||||
@Update
|
@Update
|
||||||
suspend fun updateMessage(message: ChatMessageEntity)
|
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")
|
@Query("DELETE FROM chat_messages WHERE roomId = :roomId AND createdAt < :cutoffTime")
|
||||||
suspend fun deleteOldMessages(roomId: Long, cutoffTime: Long)
|
suspend fun deleteOldMessages(roomId: Long, cutoffTime: Long)
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user