From 10004652e4bf8ee1ca256400ac8ba553cc51fd3b Mon Sep 17 00:00:00 2001 From: klaus Date: Wed, 17 Jun 2026 23:24:39 +0900 Subject: [PATCH] =?UTF-8?q?fix(creator):=20=EB=9D=BC=EC=9D=B4=EB=B8=8C=20?= =?UTF-8?q?=EB=8D=94=EB=B3=B4=EA=B8=B0=20=EC=83=81=ED=83=9C=20=EB=B3=B4?= =?UTF-8?q?=EC=A1=B4=EC=9D=84=20=EB=B3=B4=EC=A0=95=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../live/CreatorChannelLiveViewModel.kt | 12 ++- .../live/CreatorChannelLivePaginationTest.kt | 86 ++++++++++++++++++- 2 files changed, 94 insertions(+), 4 deletions(-) diff --git a/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveViewModel.kt b/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveViewModel.kt index 58b47571..5f418b32 100644 --- a/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveViewModel.kt +++ b/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveViewModel.kt @@ -58,8 +58,11 @@ class CreatorChannelLiveViewModel( val data = response.data val current = _liveStateLiveData.value as? CreatorChannelLiveUiState.Content ?: content if (response.success && data != null) { - _liveStateLiveData.value = data.toContentState( + _liveStateLiveData.value = current.copy( liveReplayContents = current.liveReplayContents + data.liveReplayContents, + page = data.page, + size = data.size, + hasNext = data.hasNext, isLoadingMore = false ) } else { @@ -71,6 +74,13 @@ class CreatorChannelLiveViewModel( } } + fun consumePaginationErrorMessage() { + val content = _liveStateLiveData.value as? CreatorChannelLiveUiState.Content ?: return + if (content.paginationErrorMessage == null) return + + _liveStateLiveData.value = content.copy(paginationErrorMessage = null) + } + private fun loadFirstPage(sort: ContentSort) { val generation = ++requestGeneration _liveStateLiveData.value = CreatorChannelLiveUiState.Loading diff --git a/app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLivePaginationTest.kt b/app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLivePaginationTest.kt index 4080a7b8..afc6a4cb 100644 --- a/app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLivePaginationTest.kt +++ b/app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLivePaginationTest.kt @@ -15,6 +15,7 @@ import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.common.SharedPreferenceManager import kr.co.vividnext.sodalive.v2.common.data.ContentSort import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelAudioContentResponse +import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelLiveResponse import kr.co.vividnext.sodalive.v2.creator.channel.data.CreatorChannelRepository import kr.co.vividnext.sodalive.v2.creator.channel.live.data.CreatorChannelLiveTabResponse import org.junit.After @@ -76,6 +77,62 @@ class CreatorChannelLivePaginationTest { verifyGetLive(page = 1) } + @Test + fun `다음 페이지 성공은 첫 페이지 현재 라이브 count sort를 보존하고 page와 hasNext만 갱신한다`() { + val currentLive = CreatorChannelLiveResponse( + liveId = 10L, + title = "현재 라이브", + coverImageUrl = null, + beginDateTimeUtc = "2026-06-17T01:00:00Z", + price = 100, + isAdult = false + ) + stubGetLive( + page = 0, + response = Single.just( + ApiResponse( + true, + liveResponse( + page = 0, + ids = listOf(1L), + hasNext = true, + liveReplayContentCount = 99, + currentLive = currentLive + ), + null + ) + ) + ) + stubGetLive( + page = 1, + response = Single.just( + ApiResponse( + true, + liveResponse( + page = 1, + sort = ContentSort.POPULAR, + ids = listOf(2L), + hasNext = false, + liveReplayContentCount = 1, + currentLive = null + ), + null + ) + ) + ) + viewModel.loadLive(100L) + + viewModel.loadMore() + + val state = viewModel.liveStateLiveData.requireValue() as CreatorChannelLiveUiState.Content + assertEquals(currentLive, state.currentLive) + assertEquals(99, state.liveReplayContentCount) + assertEquals(ContentSort.LATEST, state.selectedSort) + assertEquals(1, state.page) + assertEquals(listOf(1L, 2L), state.liveReplayContents.map { it.audioContentId }) + assertFalse(state.hasNext) + } + @Test fun `다음 페이지 로딩 중 중복 load-more 요청은 막는다`() { val pending = SingleSubject.create>() @@ -114,6 +171,27 @@ class CreatorChannelLivePaginationTest { assertEquals("failed", state.paginationErrorMessage) } + @Test + fun `pagination error message는 표시 후 clear되어 다시 표시되지 않는다`() { + stubGetLive( + page = 0, + response = Single.just(ApiResponse(true, liveResponse(page = 0, ids = listOf(1L), hasNext = true), null)) + ) + stubGetLive(page = 1, response = Single.just(ApiResponse(false, null, "failed"))) + viewModel.loadLive(100L) + viewModel.loadMore() + + val state = viewModel.liveStateLiveData.requireValue() as CreatorChannelLiveUiState.Content + + assertEquals("failed", state.paginationErrorMessage) + + viewModel.consumePaginationErrorMessage() + + val consumedState = viewModel.liveStateLiveData.requireValue() as CreatorChannelLiveUiState.Content + assertEquals(null, consumedState.paginationErrorMessage) + assertEquals(listOf(1L), consumedState.liveReplayContents.map { it.audioContentId }) + } + @Test fun `이전 load-more 응답은 이후 정렬 변경 목록에 append되지 않는다`() { val nextPagePending = SingleSubject.create>() @@ -186,10 +264,12 @@ class CreatorChannelLivePaginationTest { page: Int, sort: ContentSort = ContentSort.LATEST, ids: List, - hasNext: Boolean + hasNext: Boolean, + liveReplayContentCount: Int = ids.size, + currentLive: CreatorChannelLiveResponse? = null ) = CreatorChannelLiveTabResponse( - liveReplayContentCount = ids.size, - currentLive = null, + liveReplayContentCount = liveReplayContentCount, + currentLive = currentLive, liveReplayContents = ids.map { audioContent(it) }, sort = sort, page = page,