feat(creator): 차단 성공 후 채널에서 이탈한다

This commit is contained in:
2026-08-14 16:20:29 +09:00
parent cbce4af2f9
commit 7a21b37d8f
8 changed files with 582 additions and 1 deletions
@@ -82,6 +82,7 @@ import kr.co.vividnext.sodalive.v2.creator.channel.series.CreatorChannelSeriesFr
import kr.co.vividnext.sodalive.v2.live.action.LiveActionCoordinator
import kr.co.vividnext.sodalive.v2.live.action.LiveCreationResult
import kr.co.vividnext.sodalive.v2.live.action.resolveLiveCreationResult
import kr.co.vividnext.sodalive.v2.main.MainV2Activity
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
import org.koin.android.ext.android.inject
@@ -557,6 +558,13 @@ class CreatorChannelActivity :
handleChatAction(ChatActionCommand.AiRoom(chatRoomId))
}
override fun onCreatorChannelBlockSucceeded() {
if (isTaskRoot) {
startActivity(Intent(this, MainV2Activity::class.java))
}
finish()
}
override fun onCreatorChannelScheduleClicked(schedule: CreatorChannelScheduleResponse) {
onScheduleClicked(schedule)
}
@@ -101,6 +101,9 @@ class CreatorChannelHomeFragment : BaseFragment<FragmentCreatorChannelHomeBindin
viewModel.chatRoomIdLiveData.observe(viewLifecycleOwner) { event ->
event.consume()?.let(host::onCreatorChannelChatRoomCreated)
}
viewModel.blockSuccessLiveData.observe(viewLifecycleOwner) { event ->
event.consume()?.let { host.onCreatorChannelBlockSucceeded() }
}
viewModel.toastLiveData.observe(viewLifecycleOwner) { event ->
event.consume()?.let {
val message = it.message ?: it.resId?.let(::getString)
@@ -148,6 +151,7 @@ class CreatorChannelHomeFragment : BaseFragment<FragmentCreatorChannelHomeBindin
fun onCreatorChannelHeaderChanged(header: CreatorChannelHeaderUiModel)
fun onCreatorChannelFollowProgressChanged(inProgress: Boolean)
fun onCreatorChannelChatRoomCreated(chatRoomId: Long)
fun onCreatorChannelBlockSucceeded()
fun onCreatorChannelScheduleClicked(schedule: CreatorChannelScheduleResponse)
fun onCreatorChannelAudioContentClicked(audioContent: CreatorChannelAudioContentResponse)
fun onCreatorChannelSeriesClicked(series: CreatorChannelSeriesResponse)
@@ -30,6 +30,10 @@ class CreatorChannelHomeViewModel(
val chatRoomIdLiveData: LiveData<CreatorChannelEvent<Long>>
get() = _chatRoomIdLiveData
private val _blockSuccessLiveData = MutableLiveData<CreatorChannelEvent<Unit>>()
val blockSuccessLiveData: LiveData<CreatorChannelEvent<Unit>>
get() = _blockSuccessLiveData
private val _isFollowInProgressLiveData = MutableLiveData(false)
val isFollowInProgressLiveData: LiveData<Boolean>
get() = _isFollowInProgressLiveData
@@ -179,6 +183,7 @@ class CreatorChannelHomeViewModel(
_toastLiveData.value = CreatorChannelEvent(
ToastMessage(resId = R.string.creator_channel_block_success)
)
_blockSuccessLiveData.value = CreatorChannelEvent(Unit)
} else {
showUnknownErrorToast()
}
@@ -2457,6 +2457,43 @@ class CreatorChannelActivitySourceTest {
assertTrue(source.contains("homeActionDelegate?.refreshHome()"))
}
@Test
fun `차단 성공은 이전 화면으로 돌아가고 task root이면 MainV2Activity로 이동한다`() {
val fragmentSource = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelHomeFragment.kt"
).readText()
val activitySource = projectFile(
"app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/CreatorChannelActivity.kt"
).readText()
val observerSource = sourceSection(
source = fragmentSource,
startMarker = "private fun observeViewModel()",
endMarker = "private fun onScheduleClicked"
)
assertTrue(observerSource.contains("viewModel.blockSuccessLiveData.observe(viewLifecycleOwner)"))
assertTrue(observerSource.contains("event.consume()?.let { host.onCreatorChannelBlockSucceeded() }"))
assertTrue(fragmentSource.contains("fun onCreatorChannelBlockSucceeded()"))
val blockDialogSource = sourceSection(
source = activitySource,
startMarker = "private fun showUserBlockDialog()",
endMarker = "private fun showUserReportDialog()"
)
assertTrue(blockDialogSource.contains("homeActionDelegate?.blockUser()"))
assertFalse(blockDialogSource.contains("finish()"))
assertFalse(blockDialogSource.contains("MainV2Activity"))
val callbackSource = sourceSection(
source = activitySource,
startMarker = "override fun onCreatorChannelBlockSucceeded()",
endMarker = "override fun onCreatorChannelScheduleClicked"
)
assertTrue(callbackSource.contains("if (isTaskRoot)"))
assertTrue(callbackSource.contains("startActivity(Intent(this, MainV2Activity::class.java))"))
assertTrue(callbackSource.contains("finish()"))
}
private fun sourceSection(source: String, startMarker: String, endMarker: String): String {
val start = source.indexOf(startMarker)
val end = source.indexOf(endMarker, start)
@@ -337,7 +337,7 @@ class CreatorChannelHomeViewModelTest {
}
@Test
fun `차단 성공은 block API를 호출하고 차단 완료 토스트를 emit한다`() {
fun `차단 성공은 block API를 호출하고 차단 완료 토스트와 성공 이벤트를 emit한다`() {
whenever(repository.getHome(100L, "Bearer test-token")).thenReturn(Single.just(ApiResponse(true, response(), null)))
whenever(repository.blockUser(100L, "Bearer test-token")).thenReturn(Single.just(ApiResponse(true, Any(), null)))
viewModel.loadHome(100L)
@@ -347,6 +347,35 @@ class CreatorChannelHomeViewModelTest {
verify(repository).blockUser(100L, "Bearer test-token")
val toastEvent = viewModel.toastLiveData.requireValue()
assertEquals(R.string.creator_channel_block_success, toastEvent?.consume()?.resId)
val blockSuccessEvent = viewModel.blockSuccessLiveData.requireValue()
assertEquals(Unit, blockSuccessEvent?.consume())
assertEquals(null, blockSuccessEvent?.consume())
}
@Test
fun `차단 실패는 unknown toast를 emit하고 성공 이벤트를 emit하지 않는다`() {
whenever(repository.getHome(100L, "Bearer test-token")).thenReturn(Single.just(ApiResponse(true, response(), null)))
whenever(repository.blockUser(100L, "Bearer test-token")).thenReturn(Single.just(ApiResponse(false, null, "failed")))
viewModel.loadHome(100L)
viewModel.blockUser()
val toastEvent = viewModel.toastLiveData.requireValue()
assertEquals(R.string.common_error_unknown, toastEvent?.consume()?.resId)
assertEquals(null, viewModel.blockSuccessLiveData.value)
}
@Test
fun `차단 예외는 unknown toast를 emit하고 성공 이벤트를 emit하지 않는다`() {
whenever(repository.getHome(100L, "Bearer test-token")).thenReturn(Single.just(ApiResponse(true, response(), null)))
whenever(repository.blockUser(100L, "Bearer test-token")).thenReturn(Single.error(IllegalStateException("network")))
viewModel.loadHome(100L)
viewModel.blockUser()
val toastEvent = viewModel.toastLiveData.requireValue()
assertEquals(R.string.common_error_unknown, toastEvent?.consume()?.resId)
assertEquals(null, viewModel.blockSuccessLiveData.value)
}
@Test