feat(chat): 캐릭터 상세에서 채팅방 생성 후 ChatRoomActivity로 네비게이션 추가
- ChatRoomActivity에 EXTRA_ROOM_ID 및 newIntent 추가 - CharacterDetailActivity에서 chatRoomId 수신 시 화면 이동 처리 - 이벤트 소비 유지로 중복 네비게이션 방지
This commit is contained in:
		@@ -285,5 +285,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        <!-- Character Detail -->
 | 
					        <!-- Character Detail -->
 | 
				
			||||||
        <activity android:name=".chat.character.detail.CharacterDetailActivity" />
 | 
					        <activity android:name=".chat.character.detail.CharacterDetailActivity" />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <activity android:name=".chat.talk.room.ChatRoomActivity" />
 | 
				
			||||||
    </application>
 | 
					    </application>
 | 
				
			||||||
</manifest>
 | 
					</manifest>
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -14,6 +14,7 @@ import kr.co.vividnext.sodalive.base.BaseActivity
 | 
				
			|||||||
import kr.co.vividnext.sodalive.common.LoadingDialog
 | 
					import kr.co.vividnext.sodalive.common.LoadingDialog
 | 
				
			||||||
import kr.co.vividnext.sodalive.databinding.ActivityCharacterDetailBinding
 | 
					import kr.co.vividnext.sodalive.databinding.ActivityCharacterDetailBinding
 | 
				
			||||||
import kr.co.vividnext.sodalive.extensions.dpToPx
 | 
					import kr.co.vividnext.sodalive.extensions.dpToPx
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.talk.room.ChatRoomActivity
 | 
				
			||||||
import org.koin.androidx.viewmodel.ext.android.viewModel
 | 
					import org.koin.androidx.viewmodel.ext.android.viewModel
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CharacterDetailActivity : BaseActivity<ActivityCharacterDetailBinding>(
 | 
					class CharacterDetailActivity : BaseActivity<ActivityCharacterDetailBinding>(
 | 
				
			||||||
@@ -104,6 +105,18 @@ class CharacterDetailActivity : BaseActivity<ActivityCharacterDetailBinding>(
 | 
				
			|||||||
        binding.llPersonalityExpand.setOnClickListener {
 | 
					        binding.llPersonalityExpand.setOnClickListener {
 | 
				
			||||||
            togglePersonalityExpand()
 | 
					            togglePersonalityExpand()
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 대화하기 버튼 클릭: 채팅방 생성 API 호출
 | 
				
			||||||
 | 
					        binding.btnChat.setOnClickListener {
 | 
				
			||||||
 | 
					            val idFromState = viewModel.uiState.value?.detail?.characterId ?: 0L
 | 
				
			||||||
 | 
					            val idFromIntent = intent.getLongExtra(EXTRA_CHARACTER_ID, 0L)
 | 
				
			||||||
 | 
					            val targetId = if (idFromState > 0) idFromState else idFromIntent
 | 
				
			||||||
 | 
					            if (targetId > 0) {
 | 
				
			||||||
 | 
					                viewModel.createChatRoom(targetId)
 | 
				
			||||||
 | 
					            } else {
 | 
				
			||||||
 | 
					                showToast("잘못된 접근 입니다.")
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private fun bindObservers() {
 | 
					    private fun bindObservers() {
 | 
				
			||||||
@@ -122,6 +135,14 @@ class CharacterDetailActivity : BaseActivity<ActivityCharacterDetailBinding>(
 | 
				
			|||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // 2-1) 채팅방 생성 성공 처리 (이벤트)
 | 
				
			||||||
 | 
					            state.chatRoomId?.let { roomId ->
 | 
				
			||||||
 | 
					                showToast("채팅방이 생성되었습니다. (ID: $roomId)")
 | 
				
			||||||
 | 
					                // 생성된 채팅방으로 이동 처리
 | 
				
			||||||
 | 
					                startActivity(ChatRoomActivity.newIntent(this, roomId))
 | 
				
			||||||
 | 
					                viewModel.consumeChatRoomCreated()
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            // 3) 상세 데이터가 있을 경우에만 기존 UI 바인딩 수행
 | 
					            // 3) 상세 데이터가 있을 경우에만 기존 UI 바인딩 수행
 | 
				
			||||||
            val detail = state.detail ?: return@observe
 | 
					            val detail = state.detail ?: return@observe
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,8 +1,16 @@
 | 
				
			|||||||
package kr.co.vividnext.sodalive.chat.character.detail
 | 
					package kr.co.vividnext.sodalive.chat.character.detail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import kr.co.vividnext.sodalive.chat.character.CharacterApi
 | 
					import kr.co.vividnext.sodalive.chat.character.CharacterApi
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.talk.TalkApi
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.talk.room.CreateChatRoomRequest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class CharacterDetailRepository(private val api: CharacterApi) {
 | 
					class CharacterDetailRepository(
 | 
				
			||||||
 | 
					    private val characterApi: CharacterApi,
 | 
				
			||||||
 | 
					    private val talkApi: TalkApi
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
    fun getCharacterDetail(token: String, characterId: Long) =
 | 
					    fun getCharacterDetail(token: String, characterId: Long) =
 | 
				
			||||||
        api.getCharacterDetail(authHeader = token, characterId = characterId)
 | 
					        characterApi.getCharacterDetail(authHeader = token, characterId = characterId)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun createChatRoom(token: String, request: CreateChatRoomRequest) =
 | 
				
			||||||
 | 
					        talkApi.createChatRoom(authHeader = token, request = request)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,6 +6,7 @@ import com.orhanobut.logger.Logger
 | 
				
			|||||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
 | 
					import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
 | 
				
			||||||
import io.reactivex.rxjava3.schedulers.Schedulers
 | 
					import io.reactivex.rxjava3.schedulers.Schedulers
 | 
				
			||||||
import kr.co.vividnext.sodalive.base.BaseViewModel
 | 
					import kr.co.vividnext.sodalive.base.BaseViewModel
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.talk.room.CreateChatRoomRequest
 | 
				
			||||||
import kr.co.vividnext.sodalive.common.SharedPreferenceManager
 | 
					import kr.co.vividnext.sodalive.common.SharedPreferenceManager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/**
 | 
					/**
 | 
				
			||||||
@@ -23,7 +24,8 @@ class CharacterDetailViewModel(
 | 
				
			|||||||
    data class UiState(
 | 
					    data class UiState(
 | 
				
			||||||
        val detail: CharacterDetailResponse? = null,
 | 
					        val detail: CharacterDetailResponse? = null,
 | 
				
			||||||
        val isLoading: Boolean = false,
 | 
					        val isLoading: Boolean = false,
 | 
				
			||||||
        val error: String? = null
 | 
					        val error: String? = null,
 | 
				
			||||||
 | 
					        val chatRoomId: Long? = null
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private val _uiState = MutableLiveData(UiState())
 | 
					    private val _uiState = MutableLiveData(UiState())
 | 
				
			||||||
@@ -44,14 +46,65 @@ class CharacterDetailViewModel(
 | 
				
			|||||||
                        if (success && data != null) {
 | 
					                        if (success && data != null) {
 | 
				
			||||||
                            _uiState.value = UiState(detail = data, isLoading = false, error = null)
 | 
					                            _uiState.value = UiState(detail = data, isLoading = false, error = null)
 | 
				
			||||||
                        } else {
 | 
					                        } else {
 | 
				
			||||||
                            _uiState.value = UiState(detail = null, isLoading = false, error = response.message ?: "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
 | 
					                            _uiState.value = UiState(
 | 
				
			||||||
 | 
					                                detail = null,
 | 
				
			||||||
 | 
					                                isLoading = false,
 | 
				
			||||||
 | 
					                                error = response.message ?: "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
                    },
 | 
					                    },
 | 
				
			||||||
                    { throwable ->
 | 
					                    { throwable ->
 | 
				
			||||||
                        Logger.e(throwable, throwable.message ?: "")
 | 
					                        Logger.e(throwable, throwable.message ?: "")
 | 
				
			||||||
                        _uiState.value = UiState(detail = null, isLoading = false, error = "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요.")
 | 
					                        _uiState.value = UiState(
 | 
				
			||||||
 | 
					                            detail = null,
 | 
				
			||||||
 | 
					                            isLoading = false,
 | 
				
			||||||
 | 
					                            error = "알 수 없는 오류가 발생했습니다. 다시 시도해 주세요."
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun createChatRoom(characterId: Long) {
 | 
				
			||||||
 | 
					        // 기존 상태 유지하면서 로딩/에러/이벤트만 변경
 | 
				
			||||||
 | 
					        val current = _uiState.value
 | 
				
			||||||
 | 
					        _uiState.value = current?.copy(isLoading = true, error = null, chatRoomId = null)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        val token = "Bearer ${SharedPreferenceManager.token}"
 | 
				
			||||||
 | 
					        val request = CreateChatRoomRequest(characterId)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        compositeDisposable.add(
 | 
				
			||||||
 | 
					            repository.createChatRoom(token = token, request = request)
 | 
				
			||||||
 | 
					                .subscribeOn(Schedulers.io())
 | 
				
			||||||
 | 
					                .observeOn(AndroidSchedulers.mainThread())
 | 
				
			||||||
 | 
					                .subscribe(
 | 
				
			||||||
 | 
					                    { response ->
 | 
				
			||||||
 | 
					                        val success = response.success
 | 
				
			||||||
 | 
					                        val data = response.data
 | 
				
			||||||
 | 
					                        if (success && data != null) {
 | 
				
			||||||
 | 
					                            _uiState.value = _uiState.value?.copy(
 | 
				
			||||||
 | 
					                                isLoading = false,
 | 
				
			||||||
 | 
					                                chatRoomId = data.chatRoomId
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                        } else {
 | 
				
			||||||
 | 
					                            _uiState.value = _uiState.value?.copy(
 | 
				
			||||||
 | 
					                                isLoading = false,
 | 
				
			||||||
 | 
					                                error = response.message ?: "채팅방 생성에 실패했습니다. 다시 시도해 주세요."
 | 
				
			||||||
 | 
					                            )
 | 
				
			||||||
 | 
					                        }
 | 
				
			||||||
 | 
					                    },
 | 
				
			||||||
 | 
					                    { throwable ->
 | 
				
			||||||
 | 
					                        Logger.e(throwable, throwable.message ?: "")
 | 
				
			||||||
 | 
					                        _uiState.value = _uiState.value?.copy(
 | 
				
			||||||
 | 
					                            isLoading = false,
 | 
				
			||||||
 | 
					                            error = "채팅방 생성 중 오류가 발생했습니다. 다시 시도해 주세요."
 | 
				
			||||||
 | 
					                        )
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
 | 
					                )
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun consumeChatRoomCreated() {
 | 
				
			||||||
 | 
					        _uiState.value = _uiState.value?.copy(chatRoomId = null)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,13 +1,23 @@
 | 
				
			|||||||
package kr.co.vividnext.sodalive.chat.talk
 | 
					package kr.co.vividnext.sodalive.chat.talk
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import io.reactivex.rxjava3.core.Single
 | 
					import io.reactivex.rxjava3.core.Single
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.talk.room.CreateChatRoomRequest
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.chat.talk.room.CreateChatRoomResponse
 | 
				
			||||||
import kr.co.vividnext.sodalive.common.ApiResponse
 | 
					import kr.co.vividnext.sodalive.common.ApiResponse
 | 
				
			||||||
 | 
					import retrofit2.http.Body
 | 
				
			||||||
import retrofit2.http.GET
 | 
					import retrofit2.http.GET
 | 
				
			||||||
import retrofit2.http.Header
 | 
					import retrofit2.http.Header
 | 
				
			||||||
 | 
					import retrofit2.http.POST
 | 
				
			||||||
 | 
					
 | 
				
			||||||
interface TalkApi {
 | 
					interface TalkApi {
 | 
				
			||||||
    @GET("/api/chat/room/list")
 | 
					    @GET("/api/chat/room/list")
 | 
				
			||||||
    fun getTalkRooms(
 | 
					    fun getTalkRooms(
 | 
				
			||||||
        @Header("Authorization") authHeader: String
 | 
					        @Header("Authorization") authHeader: String
 | 
				
			||||||
    ): Single<ApiResponse<List<TalkRoom>>>
 | 
					    ): Single<ApiResponse<List<TalkRoom>>>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @POST("/api/chat/room/create")
 | 
				
			||||||
 | 
					    fun createChatRoom(
 | 
				
			||||||
 | 
					        @Header("Authorization") authHeader: String,
 | 
				
			||||||
 | 
					        @Body request: CreateChatRoomRequest
 | 
				
			||||||
 | 
					    ): Single<ApiResponse<CreateChatRoomResponse>>
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -0,0 +1,26 @@
 | 
				
			|||||||
 | 
					package kr.co.vividnext.sodalive.chat.talk.room
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import android.content.Context
 | 
				
			||||||
 | 
					import android.content.Intent
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.base.BaseActivity
 | 
				
			||||||
 | 
					import kr.co.vividnext.sodalive.databinding.ActivityChatRoomBinding
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ChatRoomActivity : BaseActivity<ActivityChatRoomBinding>(
 | 
				
			||||||
 | 
					    ActivityChatRoomBinding::inflate
 | 
				
			||||||
 | 
					) {
 | 
				
			||||||
 | 
					    override fun setupView() {
 | 
				
			||||||
 | 
					        // TODO: roomId를 활용한 채팅방 초기화 로직 추가 예정
 | 
				
			||||||
 | 
					        val roomId = intent.getLongExtra(EXTRA_ROOM_ID, 0L)
 | 
				
			||||||
 | 
					        // 필요 시 roomId 유효성 체크 및 UI 바인딩
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    companion object {
 | 
				
			||||||
 | 
					        const val EXTRA_ROOM_ID: String = "extra_room_id"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fun newIntent(context: Context, roomId: Long): Intent {
 | 
				
			||||||
 | 
					            return Intent(context, ChatRoomActivity::class.java).apply {
 | 
				
			||||||
 | 
					                putExtra(EXTRA_ROOM_ID, roomId)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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 CreateChatRoomRequest(
 | 
				
			||||||
 | 
					    @SerializedName("characterId") val characterId: Long
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
@@ -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 CreateChatRoomResponse(
 | 
				
			||||||
 | 
					    @SerializedName("chatRoomId") val chatRoomId: Long
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
@@ -400,7 +400,7 @@ class AppDI(private val context: Context, isDebugMode: Boolean) {
 | 
				
			|||||||
        factory { PointStatusRepository(get()) }
 | 
					        factory { PointStatusRepository(get()) }
 | 
				
			||||||
        factory { HomeRepository(get()) }
 | 
					        factory { HomeRepository(get()) }
 | 
				
			||||||
        factory { CharacterTabRepository(get()) }
 | 
					        factory { CharacterTabRepository(get()) }
 | 
				
			||||||
        factory { CharacterDetailRepository(get()) }
 | 
					        factory { CharacterDetailRepository(get(), get()) }
 | 
				
			||||||
        factory { TalkTabRepository(get()) }
 | 
					        factory { TalkTabRepository(get()) }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										6
									
								
								app/src/main/res/layout/activity_chat_room.xml
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								app/src/main/res/layout/activity_chat_room.xml
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,6 @@
 | 
				
			|||||||
 | 
					<?xml version="1.0" encoding="utf-8"?>
 | 
				
			||||||
 | 
					<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
 | 
				
			||||||
 | 
					    android:layout_width="match_parent"
 | 
				
			||||||
 | 
					    android:layout_height="match_parent">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					</androidx.constraintlayout.widget.ConstraintLayout>
 | 
				
			||||||
		Reference in New Issue
	
	Block a user