diff --git a/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/model/CreatorChannelLiveMappers.kt b/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/model/CreatorChannelLiveMappers.kt new file mode 100644 index 00000000..613fbeb1 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/model/CreatorChannelLiveMappers.kt @@ -0,0 +1,49 @@ +package kr.co.vividnext.sodalive.v2.creator.channel.live.model + +import androidx.annotation.StringRes +import kr.co.vividnext.sodalive.R +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.widget.AudioContentTag + +fun CreatorChannelAudioContentResponse.toReplayUiModel(): CreatorChannelLiveReplayUiModel = + CreatorChannelLiveReplayUiModel( + audioContentId = audioContentId, + title = title, + secondaryText = duration, + imageUrl = imageUrl, + price = price, + showAdultBadge = isAdult, + tags = toAudioContentTags(), + status = toReplayStatus() + ) + +@StringRes +fun ContentSort.toLabelResId(): Int = when (this) { + ContentSort.LATEST -> R.string.screen_audio_content_sort_newest + ContentSort.POPULAR -> R.string.screen_audio_content_sort_popularity + ContentSort.OWNED -> R.string.creator_channel_live_sort_owned + ContentSort.PRICE_HIGH -> R.string.screen_audio_content_sort_price_high + ContentSort.PRICE_LOW -> R.string.screen_audio_content_sort_price_low +} + +fun ContentSort.toSortOptionUiModel(selectedSort: ContentSort): CreatorChannelLiveSortOptionUiModel = + CreatorChannelLiveSortOptionUiModel( + sort = this, + labelResId = toLabelResId(), + isSelected = this == selectedSort + ) + +private fun CreatorChannelAudioContentResponse.toAudioContentTags(): Set = buildSet { + if (isOriginalSeries == true) add(AudioContentTag.Original) + if (isFirstContent) add(AudioContentTag.First) + if (isPointAvailable) add(AudioContentTag.Point) + if (price == 0) add(AudioContentTag.Free) +} + +private fun CreatorChannelAudioContentResponse.toReplayStatus(): CreatorChannelLiveReplayStatus = when { + isOwned -> CreatorChannelLiveReplayStatus.Owned + isRented -> CreatorChannelLiveReplayStatus.Rented + price == 0 -> CreatorChannelLiveReplayStatus.Play + else -> CreatorChannelLiveReplayStatus.Price(price) +} diff --git a/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/model/CreatorChannelLiveUiModels.kt b/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/model/CreatorChannelLiveUiModels.kt new file mode 100644 index 00000000..532a1ad8 --- /dev/null +++ b/app/src/main/java/kr/co/vividnext/sodalive/v2/creator/channel/live/model/CreatorChannelLiveUiModels.kt @@ -0,0 +1,29 @@ +package kr.co.vividnext.sodalive.v2.creator.channel.live.model + +import androidx.annotation.StringRes +import kr.co.vividnext.sodalive.v2.common.data.ContentSort +import kr.co.vividnext.sodalive.v2.widget.AudioContentTag + +data class CreatorChannelLiveReplayUiModel( + val audioContentId: Long, + val title: String, + val secondaryText: String?, + val imageUrl: String?, + val price: Int, + val showAdultBadge: Boolean, + val tags: Set, + val status: CreatorChannelLiveReplayStatus +) + +sealed interface CreatorChannelLiveReplayStatus { + data object Play : CreatorChannelLiveReplayStatus + data object Owned : CreatorChannelLiveReplayStatus + data object Rented : CreatorChannelLiveReplayStatus + data class Price(val price: Int) : CreatorChannelLiveReplayStatus +} + +data class CreatorChannelLiveSortOptionUiModel( + val sort: ContentSort, + @param:StringRes val labelResId: Int, + val isSelected: Boolean +) diff --git a/app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveMapperTest.kt b/app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveMapperTest.kt new file mode 100644 index 00000000..ffe08265 --- /dev/null +++ b/app/src/test/java/kr/co/vividnext/sodalive/v2/creator/channel/live/CreatorChannelLiveMapperTest.kt @@ -0,0 +1,89 @@ +package kr.co.vividnext.sodalive.v2.creator.channel.live + +import kr.co.vividnext.sodalive.R +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.live.model.CreatorChannelLiveReplayStatus +import kr.co.vividnext.sodalive.v2.creator.channel.live.model.toLabelResId +import kr.co.vividnext.sodalive.v2.creator.channel.live.model.toReplayUiModel +import kr.co.vividnext.sodalive.v2.widget.AudioContentTag +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test + +class CreatorChannelLiveMapperTest { + + @Test + fun `소장과 대여가 동시에 true이면 소장중 상태를 우선 매핑한다`() { + val item = audioContent(isOwned = true, isRented = true).toReplayUiModel() + + assertEquals(CreatorChannelLiveReplayStatus.Owned, item.status) + } + + @Test + fun `무료 콘텐츠는 무료 tag와 play CTA 상태로 매핑한다`() { + val item = audioContent(price = 0).toReplayUiModel() + + assertEquals(CreatorChannelLiveReplayStatus.Play, item.status) + assertTrue(AudioContentTag.Free in item.tags) + } + + @Test + fun `성인 콘텐츠와 포인트 가능 콘텐츠는 shield와 point tag를 표시한다`() { + val item = audioContent(isAdult = true, isPointAvailable = true).toReplayUiModel() + + assertTrue(item.showAdultBadge) + assertTrue(AudioContentTag.Point in item.tags) + } + + @Test + fun `seriesName은 secondary text에 포함하지 않는다`() { + val item = audioContent(duration = "10:00", seriesName = "시리즈명").toReplayUiModel() + + assertEquals("10:00", item.secondaryText) + assertFalse(item.secondaryText.orEmpty().contains("시리즈명")) + } + + @Test + fun `첫 콘텐츠와 오리지널 시리즈는 기존 오디오 tag 정책과 동일하게 매핑한다`() { + val item = audioContent(isFirstContent = true, isOriginalSeries = true).toReplayUiModel() + + assertTrue(AudioContentTag.First in item.tags) + assertTrue(AudioContentTag.Original in item.tags) + } + + @Test + fun `정렬 값은 label resource로 매핑한다`() { + assertEquals(R.string.screen_audio_content_sort_newest, ContentSort.LATEST.toLabelResId()) + assertEquals(R.string.screen_audio_content_sort_popularity, ContentSort.POPULAR.toLabelResId()) + assertEquals(R.string.creator_channel_live_sort_owned, ContentSort.OWNED.toLabelResId()) + assertEquals(R.string.screen_audio_content_sort_price_high, ContentSort.PRICE_HIGH.toLabelResId()) + assertEquals(R.string.screen_audio_content_sort_price_low, ContentSort.PRICE_LOW.toLabelResId()) + } + + private fun audioContent( + price: Int = 10, + isPointAvailable: Boolean = false, + isFirstContent: Boolean = false, + seriesName: String? = null, + isOriginalSeries: Boolean? = false, + isAdult: Boolean = false, + isOwned: Boolean = false, + isRented: Boolean = false, + duration: String? = null + ) = CreatorChannelAudioContentResponse( + audioContentId = 1L, + title = "다시듣기", + duration = duration, + imageUrl = "https://example.com/audio.png", + price = price, + isPointAvailable = isPointAvailable, + isFirstContent = isFirstContent, + seriesName = seriesName, + isOriginalSeries = isOriginalSeries, + isAdult = isAdult, + isOwned = isOwned, + isRented = isRented + ) +}