feat(creator): FanTalk 탭 UI 모델 매핑을 추가한다
This commit is contained in:
@@ -0,0 +1,48 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.creator.channel.fantalk.model
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.common.UtcRelativeTimeTextFormatter
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkReplyResponse
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkResponse
|
||||||
|
|
||||||
|
fun List<CreatorChannelFanTalkResponse>.toFanTalkUiModels(
|
||||||
|
relativeTimeTextFormatter: UtcRelativeTimeTextFormatter,
|
||||||
|
isOwner: Boolean,
|
||||||
|
currentUserId: Long
|
||||||
|
): List<CreatorChannelFanTalkUiModel> = map {
|
||||||
|
it.toFanTalkUiModel(relativeTimeTextFormatter, isOwner, currentUserId)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun CreatorChannelFanTalkResponse.toFanTalkUiModel(
|
||||||
|
relativeTimeTextFormatter: UtcRelativeTimeTextFormatter,
|
||||||
|
isOwner: Boolean,
|
||||||
|
currentUserId: Long
|
||||||
|
) = CreatorChannelFanTalkUiModel(
|
||||||
|
fanTalkId = fanTalkId,
|
||||||
|
writerId = writerId,
|
||||||
|
writerNickname = writerNickname,
|
||||||
|
writerProfileImageUrl = writerProfileImageUrl,
|
||||||
|
content = content,
|
||||||
|
createdAtText = relativeTimeTextFormatter.format(createdAtUtc),
|
||||||
|
reply = creatorReplies.firstOrNull()?.toReplyUiModel(relativeTimeTextFormatter),
|
||||||
|
rightAction = toRightAction(isOwner = isOwner, currentUserId = currentUserId)
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun CreatorChannelFanTalkReplyResponse.toReplyUiModel(
|
||||||
|
relativeTimeTextFormatter: UtcRelativeTimeTextFormatter
|
||||||
|
) = CreatorChannelFanTalkReplyUiModel(
|
||||||
|
fanTalkId = fanTalkId,
|
||||||
|
writerId = writerId,
|
||||||
|
writerNickname = writerNickname,
|
||||||
|
writerProfileImageUrl = writerProfileImageUrl,
|
||||||
|
content = content,
|
||||||
|
createdAtText = relativeTimeTextFormatter.format(createdAtUtc)
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun CreatorChannelFanTalkResponse.toRightAction(
|
||||||
|
isOwner: Boolean,
|
||||||
|
currentUserId: Long
|
||||||
|
): CreatorChannelFanTalkRightAction = when {
|
||||||
|
writerId == currentUserId -> CreatorChannelFanTalkRightAction.OwnerMore(showEdit = true, showDelete = true)
|
||||||
|
isOwner -> CreatorChannelFanTalkRightAction.OwnerMore(showEdit = false, showDelete = true)
|
||||||
|
else -> CreatorChannelFanTalkRightAction.Report
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.creator.channel.fantalk.model
|
||||||
|
|
||||||
|
data class CreatorChannelFanTalkUiModel(
|
||||||
|
val fanTalkId: Long,
|
||||||
|
val writerId: Long,
|
||||||
|
val writerNickname: String,
|
||||||
|
val writerProfileImageUrl: String,
|
||||||
|
val content: String,
|
||||||
|
val createdAtText: String,
|
||||||
|
val reply: CreatorChannelFanTalkReplyUiModel?,
|
||||||
|
val rightAction: CreatorChannelFanTalkRightAction
|
||||||
|
)
|
||||||
|
|
||||||
|
data class CreatorChannelFanTalkReplyUiModel(
|
||||||
|
val fanTalkId: Long,
|
||||||
|
val writerId: Long,
|
||||||
|
val writerNickname: String,
|
||||||
|
val writerProfileImageUrl: String,
|
||||||
|
val content: String,
|
||||||
|
val createdAtText: String
|
||||||
|
)
|
||||||
|
|
||||||
|
sealed interface CreatorChannelFanTalkRightAction {
|
||||||
|
data object Report : CreatorChannelFanTalkRightAction
|
||||||
|
data class OwnerMore(
|
||||||
|
val showEdit: Boolean,
|
||||||
|
val showDelete: Boolean
|
||||||
|
) : CreatorChannelFanTalkRightAction
|
||||||
|
}
|
||||||
@@ -0,0 +1,135 @@
|
|||||||
|
package kr.co.vividnext.sodalive.v2.creator.channel.fantalk
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.test.core.app.ApplicationProvider
|
||||||
|
import kr.co.vividnext.sodalive.R
|
||||||
|
import kr.co.vividnext.sodalive.common.AndroidUtcRelativeTimeTextFormatter
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkReplyResponse
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.data.CreatorChannelFanTalkResponse
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.model.CreatorChannelFanTalkRightAction
|
||||||
|
import kr.co.vividnext.sodalive.v2.creator.channel.fantalk.model.toFanTalkUiModels
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Assert.assertNull
|
||||||
|
import org.junit.Assert.assertTrue
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import org.robolectric.RobolectricTestRunner
|
||||||
|
import org.robolectric.annotation.Config
|
||||||
|
|
||||||
|
@RunWith(RobolectricTestRunner::class)
|
||||||
|
@Config(sdk = [28], application = Application::class)
|
||||||
|
class CreatorChannelFanTalkMapperTest {
|
||||||
|
|
||||||
|
private val context: Context = ApplicationProvider.getApplicationContext()
|
||||||
|
private val relativeTimeTextFormatter = AndroidUtcRelativeTimeTextFormatter(context)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `기본 필드와 상대 시간을 매핑한다`() {
|
||||||
|
val item = listOf(fanTalk(createdAtUtc = System.currentTimeMillis().toString()))
|
||||||
|
.toFanTalkUiModels(relativeTimeTextFormatter, isOwner = false, currentUserId = 99L)
|
||||||
|
.single()
|
||||||
|
|
||||||
|
assertEquals(1L, item.fanTalkId)
|
||||||
|
assertEquals(10L, item.writerId)
|
||||||
|
assertEquals("writer", item.writerNickname)
|
||||||
|
assertEquals("profile.png", item.writerProfileImageUrl)
|
||||||
|
assertEquals(context.getString(R.string.character_comment_time_just_now), item.createdAtText)
|
||||||
|
assertEquals("hello fan talk", item.content)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `creatorReplies가 비어 있으면 reply는 null이다`() {
|
||||||
|
val item = listOf(fanTalk(creatorReplies = emptyList()))
|
||||||
|
.toFanTalkUiModels(relativeTimeTextFormatter, isOwner = false, currentUserId = 99L)
|
||||||
|
.single()
|
||||||
|
|
||||||
|
assertNull(item.reply)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `creatorReplies가 여러 개이면 첫 번째 reply만 매핑한다`() {
|
||||||
|
val item = listOf(
|
||||||
|
fanTalk(
|
||||||
|
creatorReplies = listOf(
|
||||||
|
reply(fanTalkId = 2L, content = "first reply"),
|
||||||
|
reply(fanTalkId = 3L, content = "second reply")
|
||||||
|
)
|
||||||
|
)
|
||||||
|
).toFanTalkUiModels(relativeTimeTextFormatter, isOwner = false, currentUserId = 99L).single()
|
||||||
|
|
||||||
|
assertEquals(2L, item.reply?.fanTalkId)
|
||||||
|
assertEquals("first reply", item.reply?.content)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `내가 쓴 글이면 수정과 삭제 owner more action이다`() {
|
||||||
|
val item = listOf(fanTalk(writerId = 10L))
|
||||||
|
.toFanTalkUiModels(relativeTimeTextFormatter, isOwner = false, currentUserId = 10L)
|
||||||
|
.single()
|
||||||
|
|
||||||
|
val action = item.rightAction as CreatorChannelFanTalkRightAction.OwnerMore
|
||||||
|
assertTrue(action.showEdit)
|
||||||
|
assertTrue(action.showDelete)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `내 채널의 타인 글이면 삭제만 가능한 owner more action이다`() {
|
||||||
|
val item = listOf(fanTalk(writerId = 11L))
|
||||||
|
.toFanTalkUiModels(relativeTimeTextFormatter, isOwner = true, currentUserId = 10L)
|
||||||
|
.single()
|
||||||
|
|
||||||
|
val action = item.rightAction as CreatorChannelFanTalkRightAction.OwnerMore
|
||||||
|
assertEquals(false, action.showEdit)
|
||||||
|
assertTrue(action.showDelete)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `내가 쓴 글도 아니고 내 채널도 아니면 신고 action이다`() {
|
||||||
|
val item = listOf(fanTalk(writerId = 11L))
|
||||||
|
.toFanTalkUiModels(relativeTimeTextFormatter, isOwner = false, currentUserId = 10L)
|
||||||
|
.single()
|
||||||
|
|
||||||
|
assertTrue(item.rightAction is CreatorChannelFanTalkRightAction.Report)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun `원글과 답글 content가 빈 문자열이어도 item을 유지한다`() {
|
||||||
|
val item = listOf(fanTalk(content = "", creatorReplies = listOf(reply(content = ""))))
|
||||||
|
.toFanTalkUiModels(relativeTimeTextFormatter, isOwner = false, currentUserId = 99L)
|
||||||
|
.single()
|
||||||
|
|
||||||
|
assertEquals("", item.content)
|
||||||
|
assertEquals("", item.reply?.content)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fanTalk(
|
||||||
|
fanTalkId: Long = 1L,
|
||||||
|
writerId: Long = 10L,
|
||||||
|
writerProfileImageUrl: String = "profile.png",
|
||||||
|
content: String = "hello fan talk",
|
||||||
|
createdAtUtc: String = "2026-06-21T00:00:00Z",
|
||||||
|
creatorReplies: List<CreatorChannelFanTalkReplyResponse> = emptyList()
|
||||||
|
) = CreatorChannelFanTalkResponse(
|
||||||
|
fanTalkId = fanTalkId,
|
||||||
|
writerId = writerId,
|
||||||
|
writerNickname = "writer",
|
||||||
|
writerProfileImageUrl = writerProfileImageUrl,
|
||||||
|
content = content,
|
||||||
|
createdAtUtc = createdAtUtc,
|
||||||
|
creatorReplies = creatorReplies
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun reply(
|
||||||
|
fanTalkId: Long = 2L,
|
||||||
|
writerId: Long = 20L,
|
||||||
|
content: String = "reply"
|
||||||
|
) = CreatorChannelFanTalkReplyResponse(
|
||||||
|
fanTalkId = fanTalkId,
|
||||||
|
writerId = writerId,
|
||||||
|
writerNickname = "creator",
|
||||||
|
writerProfileImageUrl = "creator.png",
|
||||||
|
content = content,
|
||||||
|
createdAtUtc = "2026-06-21T00:00:00Z"
|
||||||
|
)
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user