fix(chat): 대화 초기화 성공 시 로컬 데이터 삭제 및 로딩 다이얼로그 적용

- ChatMessageDao: deleteMessagesByRoomId(roomId) 추가
- ChatRepository: clearMessagesByRoom(roomId) 추가
- ChatRoomActivity:
  - clearLocalPrefsForRoom(roomId) 구현
  - reset 플로우에 Prefs/DB 삭제 체인 연결
  - onResetChatRequested()에서 LoadingDialog 표시 및 doFinally로 닫힘 보장
This commit is contained in:
2025-08-28 00:23:14 +09:00
parent e3bcc6d3a6
commit 5c78c567ca
4 changed files with 60 additions and 8 deletions

View File

@@ -4,6 +4,7 @@ import io.reactivex.rxjava3.core.Single
import kr.co.vividnext.sodalive.chat.talk.room.ChatMessagePurchaseRequest
import kr.co.vividnext.sodalive.chat.talk.room.ChatMessagesResponse
import kr.co.vividnext.sodalive.chat.talk.room.ChatRoomEnterResponse
import kr.co.vividnext.sodalive.chat.talk.room.ChatRoomResetRequest
import kr.co.vividnext.sodalive.chat.talk.room.CreateChatRoomRequest
import kr.co.vividnext.sodalive.chat.talk.room.CreateChatRoomResponse
import kr.co.vividnext.sodalive.chat.talk.room.SendChatMessageResponse
@@ -35,7 +36,8 @@ interface TalkApi {
@POST("/api/chat/room/{roomId}/reset")
fun resetChatRoom(
@Header("Authorization") authHeader: String,
@Path("roomId") roomId: Long
@Path("roomId") roomId: Long,
@Body request: ChatRoomResetRequest
): Single<ApiResponse<CreateChatRoomResponse>>
// 통합 채팅방 입장 API

View File

@@ -43,7 +43,13 @@ class ChatRepository(
.flatMap { response ->
// 1) 로컬에 사용자 메시지 상태를 SENT로 업데이트
val updateStatus = Completable.fromAction {
kotlinx.coroutines.runBlocking { chatDao.updateStatusByLocalId(roomId, localId, MessageStatus.SENT.name) }
kotlinx.coroutines.runBlocking {
chatDao.updateStatusByLocalId(
roomId,
localId,
MessageStatus.SENT.name
)
}
}
// 2) 서버 응답 메시지들을 로컬 DB에 저장(중복 방지: 동일 ID는 REPLACE)
val insertServers = Completable.fromAction {
@@ -86,8 +92,16 @@ class ChatRepository(
* 통합 채팅방 입장: 서버 캐릭터 정보 + 최신 메시지 수신 후 로컬 DB 업데이트
* - 로컬 데이터가 없더라도 서버 응답을 기준으로 동기화
*/
fun enterChatRoom(token: String, roomId: Long, characterImageId: Long?): Single<ChatRoomEnterResponse> {
return talkApi.enterChatRoom(authHeader = token, roomId = roomId, characterImageId = characterImageId)
fun enterChatRoom(
token: String,
roomId: Long,
characterImageId: Long?
): Single<ChatRoomEnterResponse> {
return talkApi.enterChatRoom(
authHeader = token,
roomId = roomId,
characterImageId = characterImageId
)
.subscribeOn(Schedulers.io())
.flatMap { response ->
val data = ensureSuccess(response)
@@ -106,7 +120,12 @@ class ChatRepository(
* 점진적 메시지 로딩: 커서 기반으로 이전 메시지를 조회
*/
fun loadMoreMessages(token: String, roomId: Long, cursor: Long?): Single<ChatMessagesResponse> {
return talkApi.getChatRoomMessages(authHeader = token, roomId = roomId, cursor = cursor, limit = 20)
return talkApi.getChatRoomMessages(
authHeader = token,
roomId = roomId,
cursor = cursor,
limit = 20
)
.subscribeOn(Schedulers.io())
.flatMap { response ->
val data = ensureMessagesSuccess(response)
@@ -132,10 +151,20 @@ class ChatRepository(
/**
* localId 기반 상태 업데이트(SENDING→SENT/FAILED)
*/
fun updateMessageStatusByLocalId(roomId: Long, localId: String, newStatus: MessageStatus): Completable {
fun updateMessageStatusByLocalId(
roomId: Long,
localId: String,
newStatus: MessageStatus
): Completable {
return Completable.fromAction {
// enum을 문자열로 변환하여 쿼리 파라미터로 전달
kotlinx.coroutines.runBlocking { chatDao.updateStatusByLocalId(roomId, localId, newStatus.name) }
kotlinx.coroutines.runBlocking {
chatDao.updateStatusByLocalId(
roomId,
localId,
newStatus.name
)
}
}.subscribeOn(Schedulers.io())
}
@@ -206,7 +235,11 @@ class ChatRepository(
* 대화 초기화: 서버에 리셋 요청 → 새 채팅방 ID 반환
*/
fun resetChatRoom(token: String, roomId: Long): Single<CreateChatRoomResponse> {
return talkApi.resetChatRoom(authHeader = token, roomId = roomId)
return talkApi.resetChatRoom(
authHeader = token,
roomId = roomId,
request = ChatRoomResetRequest()
)
.subscribeOn(Schedulers.io())
.map { ensureSuccess(it) }
}

View File

@@ -20,6 +20,7 @@ import kr.co.vividnext.sodalive.R
import kr.co.vividnext.sodalive.base.BaseActivity
import kr.co.vividnext.sodalive.base.SodaDialog
import kr.co.vividnext.sodalive.chat.character.detail.detail.CharacterType
import kr.co.vividnext.sodalive.common.LoadingDialog
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
import kr.co.vividnext.sodalive.databinding.ActivityChatRoomBinding
import kr.co.vividnext.sodalive.extensions.dpToPx
@@ -890,6 +891,12 @@ class ChatRoomActivity : BaseActivity<ActivityChatRoomBinding>(
desc = desc,
confirmButtonTitle = "초기화",
confirmButtonClick = {
val loadingDialog = LoadingDialog(this, layoutInflater)
loadingDialog.show(
screenWidth,
"대화 초기화 중..."
)
val token = "Bearer ${SharedPreferenceManager.token}"
val disposable = chatRepository.resetChatRoom(token = token, roomId = roomId)
.flatMap { response ->
@@ -899,6 +906,7 @@ class ChatRoomActivity : BaseActivity<ActivityChatRoomBinding>(
.andThen(io.reactivex.rxjava3.core.Single.just(response))
}
.observeOn(AndroidSchedulers.mainThread())
.doFinally { loadingDialog.dismiss() }
.subscribe({ response ->
val intent = newIntent(this, response.chatRoomId)
startActivity(intent)

View File

@@ -0,0 +1,9 @@
package kr.co.vividnext.sodalive.chat.talk.room
import androidx.annotation.Keep
import com.google.gson.annotations.SerializedName
@Keep
data class ChatRoomResetRequest(
@SerializedName("container") val container: String = "aos"
)