From 09b8979ba0953c35d262c8f2a325285fe557fb44 Mon Sep 17 00:00:00 2001 From: klaus Date: Thu, 14 Aug 2025 20:23:21 +0900 Subject: [PATCH] =?UTF-8?q?feat(chat-room):=20sendMessage=20=EC=9D=91?= =?UTF-8?q?=EB=8B=B5=20=EB=8B=A4=EA=B1=B4=20=EB=B3=80=EA=B2=BD=20=EB=B0=98?= =?UTF-8?q?=EC=98=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - TalkApi.sendMessage: ApiResponse>로 변경 - ChatRepository.sendMessage: Single>로 변경. 로컬 SENDING→SENT 업데이트 후, 응답 메시지 전체를 DB에 저장 - ChatRoomActivity: 구독부에서 List를 처리하며 mine == false(AI) 메시지들만 순서대로 append. 타이핑 인디케이터는 성공/실패 시 동일하게 제거 --- .../co/vividnext/sodalive/chat/talk/TalkApi.kt | 2 +- .../sodalive/chat/talk/room/ChatRepository.kt | 16 ++++++++-------- .../sodalive/chat/talk/room/ChatRoomActivity.kt | 12 +++++++----- 3 files changed, 16 insertions(+), 14 deletions(-) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/TalkApi.kt b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/TalkApi.kt index 70b713fa..a3ea6740 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/TalkApi.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/TalkApi.kt @@ -40,7 +40,7 @@ interface TalkApi { @Header("Authorization") authHeader: String, @Path("roomId") roomId: Long, @Body request: SendMessageRequest - ): Single> + ): Single>> // 점진적 메시지 로딩 API @GET("/api/chat/rooms/{roomId}/messages") diff --git a/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRepository.kt b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRepository.kt index 6c4d0da9..80ae23f8 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRepository.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRepository.kt @@ -30,7 +30,7 @@ class ChatRepository( roomId: Long, localId: String, content: String - ): Single { + ): Single> { return talkApi.sendMessage( authHeader = token, roomId = roomId, @@ -38,18 +38,18 @@ class ChatRepository( ) .subscribeOn(Schedulers.io()) .map { ensureSuccess(it) } - .flatMap { serverMsg -> + .flatMap { serverMsgs -> // 1) 로컬에 사용자 메시지 상태를 SENT로 업데이트 val updateStatus = Completable.fromAction { kotlinx.coroutines.runBlocking { chatDao.updateStatusByLocalId(roomId, localId, MessageStatus.SENT.name) } } - // 2) 서버 응답 메시지를 로컬 DB에 저장(중복 방지: 동일 ID는 REPLACE) - val insertServer = Completable.fromAction { - val entity = serverMsg.toEntity(roomId) - kotlinx.coroutines.runBlocking { chatDao.insertMessage(entity) } + // 2) 서버 응답 메시지들을 로컬 DB에 저장(중복 방지: 동일 ID는 REPLACE) + val insertServers = Completable.fromAction { + val entities = serverMsgs.map { it.toEntity(roomId) } + kotlinx.coroutines.runBlocking { chatDao.insertMessages(entities) } } - updateStatus.andThen(insertServer) - .andThen(Single.just(serverMsg)) + updateStatus.andThen(insertServers) + .andThen(Single.just(serverMsgs)) .subscribeOn(Schedulers.io()) } } diff --git a/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRoomActivity.kt b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRoomActivity.kt index 1d8214b4..0c07d066 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRoomActivity.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/chat/talk/room/ChatRoomActivity.kt @@ -285,15 +285,17 @@ class ChatRoomActivity : BaseActivity( content = content ) .observeOn(io.reactivex.rxjava3.android.schedulers.AndroidSchedulers.mainThread()) - .subscribe({ serverMsg -> + .subscribe({ serverMsgs -> // 성공: 타이핑 인디케이터 제거 및 상태 업데이트 chatAdapter.hideTypingIndicator() updateUserMessageStatus(localId, MessageStatus.SENT) - // 서버 응답이 AI 메시지인 경우 리스트에 추가 - val domain = serverMsg.toDomain() - if (!domain.mine) { - appendMessage(ChatListItem.AiMessage(domain, characterInfo?.name)) + // 서버 응답이 여러 개인 경우, mine == false(AI)만 순서대로 추가 + serverMsgs.forEach { msg -> + val domain = msg.toDomain() + if (!domain.mine) { + appendMessage(ChatListItem.AiMessage(domain, characterInfo?.name)) + } } }, { error -> // 실패: 타이핑 인디케이터 제거 및 FAILED로 업데이트