fix(chat): 대화 초기화 성공 시 로컬 데이터 삭제 및 로딩 다이얼로그 적용
- ChatMessageDao: deleteMessagesByRoomId(roomId) 추가 - ChatRepository: clearMessagesByRoom(roomId) 추가 - ChatRoomActivity: - clearLocalPrefsForRoom(roomId) 구현 - reset 플로우에 Prefs/DB 삭제 체인 연결 - onResetChatRequested()에서 LoadingDialog 표시 및 doFinally로 닫힘 보장
This commit is contained in:
		@@ -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.ChatMessagePurchaseRequest
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.talk.room.ChatMessagesResponse
 | 
					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.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.CreateChatRoomRequest
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.talk.room.CreateChatRoomResponse
 | 
					import kr.co.vividnext.sodalive.chat.talk.room.CreateChatRoomResponse
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.talk.room.SendChatMessageResponse
 | 
					import kr.co.vividnext.sodalive.chat.talk.room.SendChatMessageResponse
 | 
				
			||||||
@@ -35,7 +36,8 @@ interface TalkApi {
 | 
				
			|||||||
    @POST("/api/chat/room/{roomId}/reset")
 | 
					    @POST("/api/chat/room/{roomId}/reset")
 | 
				
			||||||
    fun resetChatRoom(
 | 
					    fun resetChatRoom(
 | 
				
			||||||
        @Header("Authorization") authHeader: String,
 | 
					        @Header("Authorization") authHeader: String,
 | 
				
			||||||
        @Path("roomId") roomId: Long
 | 
					        @Path("roomId") roomId: Long,
 | 
				
			||||||
 | 
					        @Body request: ChatRoomResetRequest
 | 
				
			||||||
    ): Single<ApiResponse<CreateChatRoomResponse>>
 | 
					    ): Single<ApiResponse<CreateChatRoomResponse>>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 통합 채팅방 입장 API
 | 
					    // 통합 채팅방 입장 API
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,7 +43,13 @@ class ChatRepository(
 | 
				
			|||||||
            .flatMap { response ->
 | 
					            .flatMap { response ->
 | 
				
			||||||
                // 1) 로컬에 사용자 메시지 상태를 SENT로 업데이트
 | 
					                // 1) 로컬에 사용자 메시지 상태를 SENT로 업데이트
 | 
				
			||||||
                val updateStatus = Completable.fromAction {
 | 
					                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)
 | 
					                // 2) 서버 응답 메시지들을 로컬 DB에 저장(중복 방지: 동일 ID는 REPLACE)
 | 
				
			||||||
                val insertServers = Completable.fromAction {
 | 
					                val insertServers = Completable.fromAction {
 | 
				
			||||||
@@ -86,8 +92,16 @@ class ChatRepository(
 | 
				
			|||||||
     * 통합 채팅방 입장: 서버 캐릭터 정보 + 최신 메시지 수신 후 로컬 DB 업데이트
 | 
					     * 통합 채팅방 입장: 서버 캐릭터 정보 + 최신 메시지 수신 후 로컬 DB 업데이트
 | 
				
			||||||
     * - 로컬 데이터가 없더라도 서버 응답을 기준으로 동기화
 | 
					     * - 로컬 데이터가 없더라도 서버 응답을 기준으로 동기화
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    fun enterChatRoom(token: String, roomId: Long, characterImageId: Long?): Single<ChatRoomEnterResponse> {
 | 
					    fun enterChatRoom(
 | 
				
			||||||
        return talkApi.enterChatRoom(authHeader = token, roomId = roomId, characterImageId = characterImageId)
 | 
					        token: String,
 | 
				
			||||||
 | 
					        roomId: Long,
 | 
				
			||||||
 | 
					        characterImageId: Long?
 | 
				
			||||||
 | 
					    ): Single<ChatRoomEnterResponse> {
 | 
				
			||||||
 | 
					        return talkApi.enterChatRoom(
 | 
				
			||||||
 | 
					            authHeader = token,
 | 
				
			||||||
 | 
					            roomId = roomId,
 | 
				
			||||||
 | 
					            characterImageId = characterImageId
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
            .subscribeOn(Schedulers.io())
 | 
					            .subscribeOn(Schedulers.io())
 | 
				
			||||||
            .flatMap { response ->
 | 
					            .flatMap { response ->
 | 
				
			||||||
                val data = ensureSuccess(response)
 | 
					                val data = ensureSuccess(response)
 | 
				
			||||||
@@ -106,7 +120,12 @@ class ChatRepository(
 | 
				
			|||||||
     * 점진적 메시지 로딩: 커서 기반으로 이전 메시지를 조회
 | 
					     * 점진적 메시지 로딩: 커서 기반으로 이전 메시지를 조회
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    fun loadMoreMessages(token: String, roomId: Long, cursor: Long?): Single<ChatMessagesResponse> {
 | 
					    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())
 | 
					            .subscribeOn(Schedulers.io())
 | 
				
			||||||
            .flatMap { response ->
 | 
					            .flatMap { response ->
 | 
				
			||||||
                val data = ensureMessagesSuccess(response)
 | 
					                val data = ensureMessagesSuccess(response)
 | 
				
			||||||
@@ -132,10 +151,20 @@ class ChatRepository(
 | 
				
			|||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * localId 기반 상태 업데이트(SENDING→SENT/FAILED)
 | 
					     * 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 {
 | 
					        return Completable.fromAction {
 | 
				
			||||||
            // enum을 문자열로 변환하여 쿼리 파라미터로 전달
 | 
					            // enum을 문자열로 변환하여 쿼리 파라미터로 전달
 | 
				
			||||||
            kotlinx.coroutines.runBlocking { chatDao.updateStatusByLocalId(roomId, localId, newStatus.name) }
 | 
					            kotlinx.coroutines.runBlocking {
 | 
				
			||||||
 | 
					                chatDao.updateStatusByLocalId(
 | 
				
			||||||
 | 
					                    roomId,
 | 
				
			||||||
 | 
					                    localId,
 | 
				
			||||||
 | 
					                    newStatus.name
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }.subscribeOn(Schedulers.io())
 | 
					        }.subscribeOn(Schedulers.io())
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -206,7 +235,11 @@ class ChatRepository(
 | 
				
			|||||||
     * 대화 초기화: 서버에 리셋 요청 → 새 채팅방 ID 반환
 | 
					     * 대화 초기화: 서버에 리셋 요청 → 새 채팅방 ID 반환
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    fun resetChatRoom(token: String, roomId: Long): Single<CreateChatRoomResponse> {
 | 
					    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())
 | 
					            .subscribeOn(Schedulers.io())
 | 
				
			||||||
            .map { ensureSuccess(it) }
 | 
					            .map { ensureSuccess(it) }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -20,6 +20,7 @@ import kr.co.vividnext.sodalive.R
 | 
				
			|||||||
import kr.co.vividnext.sodalive.base.BaseActivity
 | 
					import kr.co.vividnext.sodalive.base.BaseActivity
 | 
				
			||||||
import kr.co.vividnext.sodalive.base.SodaDialog
 | 
					import kr.co.vividnext.sodalive.base.SodaDialog
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.character.detail.detail.CharacterType
 | 
					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.common.SharedPreferenceManager
 | 
				
			||||||
import kr.co.vividnext.sodalive.databinding.ActivityChatRoomBinding
 | 
					import kr.co.vividnext.sodalive.databinding.ActivityChatRoomBinding
 | 
				
			||||||
import kr.co.vividnext.sodalive.extensions.dpToPx
 | 
					import kr.co.vividnext.sodalive.extensions.dpToPx
 | 
				
			||||||
@@ -890,6 +891,12 @@ class ChatRoomActivity : BaseActivity<ActivityChatRoomBinding>(
 | 
				
			|||||||
            desc = desc,
 | 
					            desc = desc,
 | 
				
			||||||
            confirmButtonTitle = "초기화",
 | 
					            confirmButtonTitle = "초기화",
 | 
				
			||||||
            confirmButtonClick = {
 | 
					            confirmButtonClick = {
 | 
				
			||||||
 | 
					                val loadingDialog = LoadingDialog(this, layoutInflater)
 | 
				
			||||||
 | 
					                loadingDialog.show(
 | 
				
			||||||
 | 
					                    screenWidth,
 | 
				
			||||||
 | 
					                    "대화 초기화 중..."
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                val token = "Bearer ${SharedPreferenceManager.token}"
 | 
					                val token = "Bearer ${SharedPreferenceManager.token}"
 | 
				
			||||||
                val disposable = chatRepository.resetChatRoom(token = token, roomId = roomId)
 | 
					                val disposable = chatRepository.resetChatRoom(token = token, roomId = roomId)
 | 
				
			||||||
                    .flatMap { response ->
 | 
					                    .flatMap { response ->
 | 
				
			||||||
@@ -899,6 +906,7 @@ class ChatRoomActivity : BaseActivity<ActivityChatRoomBinding>(
 | 
				
			|||||||
                            .andThen(io.reactivex.rxjava3.core.Single.just(response))
 | 
					                            .andThen(io.reactivex.rxjava3.core.Single.just(response))
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    .observeOn(AndroidSchedulers.mainThread())
 | 
					                    .observeOn(AndroidSchedulers.mainThread())
 | 
				
			||||||
 | 
					                    .doFinally { loadingDialog.dismiss() }
 | 
				
			||||||
                    .subscribe({ response ->
 | 
					                    .subscribe({ response ->
 | 
				
			||||||
                        val intent = newIntent(this, response.chatRoomId)
 | 
					                        val intent = newIntent(this, response.chatRoomId)
 | 
				
			||||||
                        startActivity(intent)
 | 
					                        startActivity(intent)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
		Reference in New Issue
	
	Block a user