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